простые функции Haskell в бессмысленном стиле - PullRequest
12 голосов
/ 11 декабря 2011

Я пытаюсь понять, как преобразовать функции в нотации без точек в Haskell.Я видел этот пример , но он сложнее, чем я ищу.Мне кажется, что я понимаю логику, стоящую за этим, но когда я пытаюсь выполнить несколько простых примеров в коде, я получаю ошибки компиляции.Я хочу попробовать написать эту функцию в стиле без точек:

f x = 5 + 8/x, который я переставил как f x = (+) 5 $ (/) 8 x

Итак, я подумал, что это может быть что-то вроде этого:

f = (+) 5 $ (/) 8

но когда я запускаю это в ghci, я получаю это сообщение:

No instance for (Num (a0 -> a0))
  arising from the literal `5' at Test.hs:3:9
Possible fix: add an instance declaration for (Num (a0 -> a0))
In the first argument of `(+)', namely `5'
In the first argument of `($)', namely `(+) 5'
In the expression: (+) 5 $ (/) 8
Failed, modules loaded: none.

Я не понимаю сообщения "Нет экземпляра для ...".Что мне нужно сделать, чтобы написать эту функцию в стиле без точек?

Ответы [ 4 ]

17 голосов
/ 11 декабря 2011

$ имеет очень низкий приоритет. Итак, f x = (+) 5 $ (/) 8 x на самом деле означает f x = (+) 5 $ ((/) 8 x). Вместо этого перепишите это как

f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) .  ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)

Последнее выражение имеет смысл: f - это композиция двух операций, сначала делим 8 на то, что есть, а затем прибавляем 5 к результату. Помните, g.h означает «применить h, а затем применить g к результату».

11 голосов
/ 12 декабря 2011

Преобразование из лямбда-исчисления (которое Haskell является вариантом) терминов в термины SKI (абсолютно точечные функции, используя только const ( K ), id ( I * 1006)*) и <*> ( S )) можно выполнить по следующим простым правилам:

  1. \x -> x переводится как id;
  2. \x -> y без x в y переводится в const y;
  3. \x -> f g переводится в f' <*> g', где
    • f' - перевод \x -> f и
    • g' - перевод \x -> g.

Теперь вы можете задаться вопросом, откуда появляется .. Существует особый случайпоследний перевод: если f не имеет свободных вхождений x, то \x -> f g преобразуется в const f <*> (\x -> g), что равно f . (\x -> g).

Используя эти правила, мы можем преобразовать вашифункция:

f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f)
((+) 5) . ((/) 8)

Этап-сокращение не требуется для завершения перевода, но без него мы получили бы что-то более грязное.Например, последний шаг даст вместо этого ((+) 5) . ((/) 8) . id.

11 голосов
/ 11 декабря 2011

Программа "pointfree" может быть установлена ​​с помощью cabal install pointfree и показывает, как написать выражение в стиле pointfree. Например:

$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)

Объяснение этого преобразования:

  1. Вы можете использовать «разделы» для инфиксных / операторских функций. (a +) == \b -> a + b и (+ a) == \b -> b + a
  2. Функция . берет результат второго параметра, который является функцией с одним аргументом, и применяет его к первому аргументу.
4 голосов
/ 12 декабря 2011

Вы были действительно близко.Позвольте мне добавить еще один $ для иллюстрации:

f x = (+) 5 $ (/) 8 $ x

Должно быть понятно, что выражение (+) 5 является функцией, которая принимает один числовой ввод и производит числовой вывод.То же самое относится и к выражению (/) 8.Таким образом, вы берете любое число, введенное x, и сначала применяете (/) 8 "функцию", а затем применяете (+) 5 "функцию".

Всякий раз, когда у вас есть цепочка функций, разделенных $, вы можете заменить все, кроме самого правого, на . Это означает, что если у вас есть a $ b $ c $ d, это эквивалентно a . b . c $ d.

f x = (+) 5 . (/) 8 $ x

На этом этапе давайте на самом деле удалить $ и вместо него скобки.

f x = ((+) 5 . (/) 8) x

Теперь должно быть ясно, что вы можете удалить трейлинг x с обеих сторон:

f = (+) 5 . (/) 8

Это является основной идеей.Если у вас есть f x = expr x, вы можете уменьшить его до f = expr.Чтобы создать точечный код, вам нужно просто узнать, как большая функция состоит из более мелких функций.Частичное применение иногда необходимо для бессмысленного кода (как в этом случае, (+) 5 и (/) 8 применяются частично).Программа "pointfree" очень полезна, когда вы не хотите думать об этом;Lambdabot на канале #haskell irc использует эту программу в качестве плагина, поэтому вам даже не нужно устанавливать ее самостоятельно;просто спросите:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
...