Без точек в Haskell

У меня есть этот код, который я хочу сделать без точек;

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

Как я делаю это?

Также есть ли некоторые общие правила для точки, свободный стиль кроме "думает об этой AMD, придумывает что-то"?

17
задан Don Stewart 18 April 2011 в 22:59
поделиться

4 ответа

Чтобы превратить функцию

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*a
  4. Взять результат mod 26
  5. Add a
  6. Apply chr

Итак, в качестве первой попытки упрощения мы получаем:

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

Обратите внимание, что вы можете избежать flip, используя раздел на mod, а разделы, использующие -, становятся беспорядочными в Haskell, поэтому есть функция вычитать (они противоречат синтаксису для записи отрицательных чисел: (-2) означает отрицательный 2 и не то же самое, что вычитать 2).

В этой функции ord k + ord t является отличным кандидатом для использования Data.Function.on (link). Этот полезный комбинатор позволяет нам заменить 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)

К сожалению, Haskell немного беспорядочный, когда дело доходит до составления двоичной функции с последовательностью унарных функций, но есть трюк (я посмотрю, смогу ли я найти хорошую ссылку на него), и мы в конечном итоге получим:

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 = вычесть . ord

52
ответ дан 30 November 2019 в 10:09
поделиться

Подключитесь к IRC, #haskell и спросите lambdabot! :

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
0
ответ дан 30 November 2019 в 10:09
поделиться

Также есть некоторые общие правила для стиля без точек, кроме «подумайте об этом и придумайте что-нибудь»?

Вы всегда можете схитрить и используйте инструмент "pl" от lambdabot (либо перейдя на #haskell на freenode, либо используя, например, ghci on acid ). Для вашего кода pl дает:

((chr. (A +). Flip mod 26).). флип флип (2 * а). ((-).). (. ord). (+). ord

Что на самом деле не является улучшением, если вы спросите меня.

10
ответ дан 30 November 2019 в 10:09
поделиться

Определенно существует набор уловок для преобразования выражения в стиль без точек. Я не претендую на звание эксперта, но вот несколько советов.

Во-первых, вы хотите изолировать аргументы функции в самом правом члене выражения. Вашими основными инструментами здесь будут 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 , в один большой функциональный член, поэтому что у нас есть выражение вида (\ kt -> fkt) . Здесь все становится немного ошеломляющим. Для начала обратите внимание, что все члены до последнего $ являются функциями с одним аргументом, поэтому мы можем составить их:

(\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))
3
ответ дан 30 November 2019 в 10:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: