Частичное применение слева направо - PullRequest
13 голосов
/ 16 июля 2011

Вчера я начал с haskell, и все еще полностью потерян на берегу этого дивного нового мира. Теперь я столкнулся со следующей проблемой:

Предположим, у меня есть какая-то функция, которая делает магию целому числу и другой переменной:

makeTuple :: Int -> a -> (Int, a)
makeTuple n x = (n, x)

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

makeTupleList :: Int -> [a] -> [ (Int, a) ]
makeTupleList n x = map (makeTuple n) x

Насколько я понимаю, двоичная функция makeTuple применяется частично с целым числом n и, следовательно, становится унарной функцией, которая может быть отображена на каждый элемент x. Пока все хорошо.

Но что мне делать, когда функция makeTuple имеет другую сигнатуру, например:

makeTuple2 :: a -> Int -> (Int, a)
makeTuple2 x n = (n, x)

Многие пути ведут в Рим: эффект тот же, но путь другой. Теперь очевидно, что отображение больше не работает: функция ожидает Int и получает a.

makeTupleList2 :: Int -> [a] -> [ (Int, a) ]
makeTupleList2 n x = map (makeTuple2 n) x -- boolshit

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

makeTupleList2 :: Int -> [a] -> [ (Int, a) ]
makeTupleList2 n x = map (\x -> makeTuple2 x n) x

Вопрос: Каков предпочтительный функциональный способ в стиле haskell для частичного применения функций, когда парциально применяемые параметры не крайние слева?

Ответы [ 4 ]

16 голосов
/ 16 июля 2011

Вы можете использовать flip, который меняет первый и второй аргументы функции.

makeTupleList2 n x = map (flip makeTuple2 n) x

Другой вариант - использовать синтаксис обратных ссылок, чтобы создать оператор инфикса, а затем частично применить его, используяраздел оператора.

maleTupleList2 n x = map (`makeTuple2` n) x

Или, как вы сказали, мы можем использовать лямбда-выражение.Какой из них использовать, зависит от контекста и личного вкуса.Используйте то, что вам кажется наиболее понятным.


PS: То, что вы делаете, называется частичное применение .Каррирование - это процесс преобразования функции с несколькими аргументами (a, b) -> c в карри в форме a -> b -> c, чтобы ее можно было частично применить.

1 голос
/ 16 июля 2011

Если ваши функции являются только конструкторами кортежей:

makeTuple x y = (x,y)

(что также можно записать как makeTuple = (,)), тогда для этого есть специальное расширение:

{-# LANGUAGE TupleSections #-}
makeTupleList2 n x = map (n,) x
makeTupleList2' n x = map (,n) x     -- Use n as the second component

, котороетакже можно записать как

makeTupleList2 n = map (n,)
makeTupleList2' n = map (,n)

В противном случае используйте уже предложенные способы.

1 голос
/ 16 июля 2011

В этом конкретном случае вы можете использовать flip makeTuple2 n, но это работает только для функций с двумя аргументами.Но, как правило, я не нахожу вашего решения с лямбдой без запутывания или с чрезмерным питонизмом.

1 голос
/ 16 июля 2011

Вы можете заменить \x -> makeTuple2 x n на flip makeTuple2 n, потому что Prelude определяет flip как: (моя реализация, а не их)

flip :: (a -> b -> c) -> b -> a -> c
flip f y x = f x y

Следовательно, мы получаем

makeTupleList2' = map . flip makeTuple2

Или, учитывая, что это просто кортеж:

makeTupleList2'' = map . (,)

Также обратите внимание (я не уверен, насколько это эффективно), вы можете использовать zip:

makeTupleList2''' :: a -> [b] -> [(a, b)]
makeTupleList2''' = zip . repeat
...