Point Бесплатные проблемы в Haskell

Я пытаюсь преобразовать следующий код haskell в свободный стиль, безрезультатно.

bar f g xs = filter f (map g xs )

Я новичок в haskell, и любая помощь будет отличной

Ответы

Ответ 1

Преобразование в бесстольный стиль может быть сделано полностью механически, хотя и трудно, без того, чтобы быть удобным с основами синтаксиса Haskell, такими как приложение с левой ассоциативной функцией, и x + y совпадают с (+) x y. Я предполагаю, что вам удобно синтаксис Haskell; Если нет, я предлагаю сначала сначала несколько глав LYAH.

Вам нужны следующие комбинаторы, которые находятся в стандартной библиотеке. Я также дал свои стандартные имена из расчета комбинатора.

id :: a -> a                                   -- I
const :: a -> b -> a                           -- K
(.) :: (b -> c) -> (a -> b) -> (a -> c)        -- B
flip :: (a -> b -> c) -> (b -> a -> c)         -- C
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S

Работайте с одним параметром за раз. Переместите параметры слева на лямбды справа, например.

f x y = Z

становится

f = \x -> \y -> Z

Мне нравится делать этот один аргумент за раз, а не сразу, он просто выглядит чище.

Затем устраните лямбду, которую вы только что создали, в соответствии со следующими правилами. Я буду использовать строчные буквы для буквенных переменных, заглавные буквы для обозначения более сложных выражений.

  • Если у вас \x -> x, замените на id
  • Если у вас \x -> A, где A - любое выражение, в котором x не встречается, замените на const A
  • Если у вас \x -> A x, где x не встречается в A, замените на A. Это известно как "сокращение эта".
  • Если у вас \x -> A B, тогда
    • Если x встречается как в A, так и в B, замените на (\x -> A) <*> (\x -> B).
    • Если x встречается только в A, замените на flip (\x -> A) B
    • Если x происходит только в B, замените на A . (\x -> B),
    • Если x не встречается ни в A, ни в B, ну, еще одно правило, которое мы должны были использовать уже.

И затем работайте внутри, устраняя лямбды, которые вы создали. Давайте работать с этим примером:

f x y z = foo z (bar x y)
-- Move parameter to lambda:
f x y = \z -> foo z (bar x y)
-- Remember that application is left-associative, so this is the same as
f x y = \z -> (foo z) (bar x y)
-- z appears on the left and not on the right, use flip
f x y = flip (\z -> foo z) (bar x y)
-- Use rule (3) 
f x y = flip foo (bar x y)

-- Next parameter
f x = \y -> flip foo (bar x y)
-- Application is left-associative
f x = \y -> (flip foo) (bar x y)
-- y occurs on the right but not the left, use (.)
f x = flip foo . (\y -> bar x y)
-- Use rule 3
f x = flip foo . bar x

-- Next parameter
f = \x -> flip foo . bar x
-- We need to rewrite this operator into normal application style
f = \x -> (.) (flip foo) (bar x)
-- Application is left-associative
f = \x -> ((.) (flip foo)) (bar x)
-- x appears on the right but not the left, use (.)
f = ((.) (flip foo)) . (\x -> bar x)
-- use rule (3)
f = ((.) (flip foo)) . bar
-- Redundant parentheses
f = (.) (flip foo) . bar

Иди сюда, попробуй! На самом деле нет никакой уловки в определении того, какое правило использовать: используйте любое применимое правило, и вы достигнете прогресса.

Ответ 2

Оба существующих ответа на самом деле не отвечают на ваш конкретный вопрос таким образом, который разъясняет: "Вот правила, выработайте для себя", а другой - "вот ответ, нет информации о том, как правила генерируют его".

Первые три шага очень легки и состоят в удалении общего x из чего-то из формы h x = f (g x), написав h = f . g. По существу, он говорит: "Если вы можете написать вещь в форме a $ b $ c $ ... $ y $ z, и вы хотите удалить z, измените все доллары на точки, a . b . c . ... . y:

bar f g xs = filter f (map g xs)
           = filter f $ (map g xs)
           = filter f $ map g $ xs -- because a $ b $ c == a $ (b $ c).
bar f g    = filter f . map g
           = (filter f .) (map g)
           = (filter f .) $ map $ g
bar f      = (filter f .) . map

Итак, последняя f является единственной сложной частью, и это сложно, потому что f не находится в конце "выражения". Но, смотря на это, мы видим, что это раздел функции (. map), применяемый к остальной части выражения:

bar f = (.) (filter f) . map
bar f = (. map) $ (.) $ filter $ f
bar   = (. map) . (.) . filter

и то, как вы уменьшаете выражение, когда у вас нет сложных вещей, таких как f x x и тому подобное, которые появляются в нем. В общем случае существует функция flip f x y = f y x, которая "переворачивает аргументы"; вы всегда можете использовать это, чтобы переместить f на другую сторону. Здесь мы имеем flip (.) map . (.) . filter, если вы включаете явный флип-вызов.

Ответ 3

Я попросил lambdabot, робота, который зависает на разных каналах IRC Haskell, автоматически выработать эквивалент без точек. Команда @pl (бессмысленно).

10:41 <frase> @pl bar f g xs = filter f (map g xs )
10:41 <lambdabot> bar = (. map) . (.) . filter

Точная версия bar:

bar = (. map) . (.) . filter

Это, возможно, менее понятно, чем исходный (неточечный) код. Используйте свое здравое суждение при принятии решения о том, использовать ли стиль без точек в каждом конкретном случае.

Наконец, если вам неинтересно IRC, есть веб-точка без ссылок преобразователи, такие как Blunt (by @TaylorFausak), pointfree и других инструментов.