Понимание функции карты с составом функции - PullRequest
0 голосов
/ 14 июня 2019

У меня есть следующее выражение Haskell:

let increment x = 1 + x
in \xs -> map chr (map increment (map ord xs))

Я знаю, что вышеприведенное выражение берет список и применяет функцию ord к каждому элементу в списке. Для списка символов, результатом будет список целых чисел (значение ASCII). Затем функция increment увеличивает каждое целое число в списке на 1. Функция chr затем преобразует каждое целое число обратно в соответствующий ему символ.

И у меня также есть следующее выражение:

map chr . map (1+) . map ord

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

map chr . map (1+) . map ord ['a', 'b', 'c', 'd', 'e']

Я не уверен, почему выражение не сработало. Поскольку используется состав функции, выражение не будет оцениваться как:

(map chr (map (1+) (map ord ['a', 'b', 'c', 'd', 'e'])))

Когда map (1+) получает список в результате map ord, а map chr получает список в результате map (1+)?

1 Ответ

5 голосов
/ 14 июня 2019

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

map chr . map (1+) . map ord ['a', 'b', 'c', 'd', 'e']

анализируется как:

(map chr) . (map (1+)) . (map ord ['a', 'b', 'c', 'd', 'e'])

И это не имеет никакого смысла - оператор . объединяет 2 функции, а map ord ['a', 'b', 'c', 'd', 'e'] - это не функция, а список чисел.

Но есть еще лучшие альтернативы вашей правильной версии со всеми вложенными скобками. То, что вы хотите сделать, это начать со списка символов и последовательно map 3 функции над ним. Это, по определению композиции функций, аналогично применению к ней композиции 3 map s, то есть:

(map chr . map (1+) . map ord) ['a', 'b', 'c', 'd', 'e']

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

map chr . map (1+) . map ord $ ['a', 'b', 'c', 'd', 'e']

И, поскольку map f . map g всегда совпадает с map (f . g) (это должно быть очевидно, когда вы перестанете думать о том, что означают эти выражения - это также основной закон, которому должна удовлетворять операция map, чтобы сделать Тип списка в Functor), это можно записать как:

map (chr . (1+) . ord) ['a', 'b', 'c', 'd', 'e']

На мой взгляд, это лучшая и наиболее читаемая версия.

Хотя еще лучше, как снова отмечает @chi, использовать chr . (1+) . ord эквивалентно succ, встроенной функции для получения «следующего» значения типа, который может быть перечислен. Так что вы можете просто написать map succ ['a', 'b', 'c', 'd', 'e']

...