سؤال

لدي هذا الرمز الذي أريد أن أجعله خاليًا من النقاط ؛

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))

كيف يمكنني فعل ذلك؟

هل هناك أيضًا بعض القواعد العامة لأسلوب خالي من النقاط بخلاف "التفكير في هذا AMD توصل إلى شيء ما"؟

هل كانت مفيدة؟

المحلول

لتحويل وظيفة

func x y z = (some expression in x, y and z)

في شكل خالي من النقاط ، أحاول عمومًا متابعة ما يتم بالمعلمة الأخيرة z واكتب الوظيفة باسم

func x y z = (some function pipeline built using x and y) z

ثم يمكنني إلغاء zS للحصول على

func x y = (some function pipeline built using x and y)

ثم يجب أن ينتهي تكرار عملية Y و X func في شكل خالي من النقاط. التحول الأساسي للاعتراف في هذه العملية هو:

    f z = foo $ bar z    -- or f z = foo (bar z)
<=> f z = foo . bar $ z
<=> f   = foo . bar

من المهم أيضًا أن تتذكر أنه من خلال التقييم الجزئي ، يمكنك "إيقاف" الحجة الأخيرة إلى وظيفة:

foo $ bar x y == foo . bar x $ y    -- foo applied to ((bar x) applied to y)

لوظيفتك الخاصة ، فكر في التدفق k و t اذهب عبر:

  1. يتقدم ord لكل منهم
  2. أضف النتائج
  3. طرح 2*أ
  4. خذ النتيجة وزارة الدفاع 26
  5. أضف
  6. يتقدم chr

لذلك كمحاولة أولى للتبسيط ، نحصل على:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t

لاحظ أنه يمكنك تجنب flip باستخدام قسم في mod, والأقسام باستخدام - الحصول على فوضى في هاسكل لذلك هناك أ subtract الوظيفة (تصطدم مع بناء الجملة لكتابة الأرقام السلبية: (-2) يعني سلبي 2 ، وليس هو نفسه subtract 2).

في هذه الوظيفة ، ord k + ord t هو مرشح ممتاز لاستخدامه Data.Function.on (حلقة الوصل). يتيح لنا هذا combinator المفيد الاستبدال ord k + ord t مع وظيفة تطبيق على k و t:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ((+) `on` ord) k t

نحن الآن قريبون جدًا من وجود

func k t = (function pipeline) k t

وبالتالي

func = (function pipeline)

لسوء الحظ ، فإن هاسكل فوضوي بعض الشيء عندما يتعلق الأمر بتأليف وظيفة ثنائية مع سلسلة من وظائف أحادية ، ولكن هناك خدعة (سأرى ما إذا كان بإمكاني العثور على مرجع جيد لذلك) ، وينتهي بنا الأمر إلى:

import Data.Function (on)

func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)

وهو تقريبا خط أنابيب دالة خالية من النقاط لطيفة ، باستثناء تلك الخدعة القبيحة. من خلال تحديد .: اقترح المشغل في التعليقات على هذه الصفحة, ، هذا الترتيب قليلا إلى:

import Data.Function (on)

(.:) = (.).(.)

func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)

لتلميع هذا أكثر من ذلك ، يمكنك إضافة بعض وظائف المساعد لفصل الحرف <-> int من قيصر الشفرات علم الحساب. فمثلا: letterToInt = subtract a . ord

نصائح أخرى

هل هناك أيضًا بعض القواعد العامة لأسلوب خالي من النقاط بخلاف "التفكير في هذا AMD توصل إلى شيء ما"؟

يمكنك دائمًا الغش واستخدام أداة "PL" من Lambdabot (إما عن طريق الانتقال إلى #Haskell على Freenode أو باستخدام EG GHCI على الحمض). للحصول على الرمز الخاص بك يعطي:

((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord

هذا ليس تحسنا حقًا إذا سألتني.

هناك بالتأكيد مجموعة من الحيل لتحويل التعبير إلى نمط خالي من النقاط. لا أدعي أنني خبير ، ولكن إليك بعض النصائح.

أولاً ، تريد عزل وسيطات الوظيفة في مصطلح التعبير الأيمن. ستكون أدواتك الرئيسية هنا flip و $, ، باستخدام القواعد:

f a b ==> flip f b a
f (g a) ==> f $ g a

أين f و g هي وظائف ، و a و b هي تعبيرات. حتى تبدأ:

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
-- replace parens with ($)
(\k t -> chr $ (a +) . flip mod 26 $ ord k + ord t - 2*a)
-- prefix and flip (-)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ord k + ord t)
-- prefix (+)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ (+) (ord k) (ord t))

الآن نحن بحاجة إلى الحصول على t خارج على الجانب الأيمن. للقيام بذلك ، استخدم القاعدة:

f (g a) ==> (f . g) a

وهكذا:

-- pull the t out on the rhs
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((+) (ord k) . ord) t)
-- flip (.) (using a section)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) $ (+) (ord k)) t)
-- pull the k out
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

الآن ، نحتاج إلى تحويل كل شيء إلى يسار k و t في مصطلح وظيفة واحدة كبيرة ، بحيث يكون لدينا تعبير عن النموذج (\k t -> f k t). هذا هو المكان الذي تحصل فيه الأمور على الانحناء. لتبدأ ، لاحظ أن جميع المصطلحات تصل إلى الأخير $ هي وظائف ذات وسيطة واحدة ، حتى نتمكن من تأليفها:

(\k t -> chr . (a +) . flip mod 26 . flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

الآن ، لدينا وظيفة من النوع Char -> Char -> Int التي نريد تكوينها مع وظيفة النوع Int -> Char, ، تسفر وظيفة من النوع Char -> Char -> Char. يمكننا تحقيق ذلك باستخدام القاعدة (ذات المظهر الغريب للغاية)

f (g a b) ==> ((f .) . g) a b

هذا يعطينا:

(\k t -> (((chr . (a +) . flip mod 26 . flip (-) (2*a)) .) . ((. ord) . ((+) . ord))) k t)

الآن يمكننا فقط تطبيق تخفيض النسخة التجريبية:

((chr . (a +) . flip mod 26) .) . (flip flip (2*a) . ((-) . ) . ((. ord) . (+) .ord))

أفترض أن نقطة خالية من النقاط الخاصة بك هي جعل الكود أكثر إيجازًا وأكثر قابلية للقراءة. لذلك أعتقد أنه من الحكمة أيضًا القيام ببعض عمليات إعادة التجديد الأخرى نحو التبسيط والتي قد تسهل إزالة المتغيرات.

(\k t -> chr $ a + flip mod 26 (ord k + ord t - 2*a))

بادئ ذي بدء ، flip غير ضروري:

(\k t -> chr $ a + (ord k + ord t - 2*a) `mod` 26)

بعد ذلك ، أود استخدام الاسم والقهر لتوضيح وظيفة فرعية قابلة للاستخدام بشكل مستقل:

encode_characters k t = chr $ encode (ord k) (ord t)
encode x y = (x + y - 2*a) `mod` 26 + a

أعطيت أيضًا اسمًا إلى أول تعبير لجعله أكثر وضوحًا وقابل لإعادة الاستخدام. encode_characters من السهل الآن جعل النقطة خالية من النقاط باستخدام التقنية من NeFrubyr:

encode_characters = chr . encode `on` ord

بالنسبة للتعبير الثاني ، لا يمكنني إنتاج نموذج يمكن قراءته أكثر من أي موضح في الإجابات الأخرى وكلهم أقل قابلية للقراءة من النموذج النقطي. لذلك أود أن أقترح التوقف عن إعادة البناء في هذه المرحلة والاستمتاع بالنظافة وإعادة استخدام الكود الناتج.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PS: كتمرين ، اعتمادًا على سياق المشكلة ، قد يؤدي بعض التعديل الطفيف لواجهات الوظيفة (ما هي البيانات في الشكل الذي يتم تمريره إلى الوظائف) إلى مزيد من التبسيط من خلال تعميم المشكلة.

أ. تنفيذ وتبسيط الوظيفة encode_n_characters :: [Char] -> Char أين encode_characters k t = encode_n_characters [k, t]. هل النتيجة أبسط من وظيفة الحوليتين المتخصصة؟

ب. تنفيذ وظيفة encode' محددة عبر encode' (x + y) = encode x y والاسترداد encode_characters باستخدام هذه الوظيفة. هل تصبح أي من الوظائف أكثر بساطة؟ هل التنفيذ أبسط بشكل عام؟ هو encode' أكثر أو أقل قابلة لإعادة الاستخدام من encode?

الاتصال على IRC ، #Haskell, ، و اسأل Lambdabot!:

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top