Частичное приложение слева направо
Я начал с 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
Этого следовало ожидать. My -maybe слишком pythonic-обходной путь использует другую функцию для передачи параметров, куда они должны идти:
makeTupleList2 :: Int -> [a] -> [ (Int, a) ]
makeTupleList2 n x = map (\x -> makeTuple2 x n) x
Вопрос:
Каков предпочтительный функционал, стиль стиля в стиле haskell для частичного применения функций, когда парионно применяемые параметры не являются самыми левыми?
Ответы
Ответ 1
Вы можете использовать flip
, который меняет первый и второй аргументы функции.
makeTupleList2 n x = map (flip makeTuple2 n) x
Другой вариант - использовать синтаксис backticks для создания оператора infix и затем частично применять его с помощью секции оператора.
maleTupleList2 n x = map (`makeTuple2` n) x
Или, как вы сказали, мы можем использовать выражение лямбда. Какой из них использовать зависит от контекста и личного вкуса. Используйте все, что вы считаете наиболее ясным.
PS: То, что вы делаете, называется частичным приложением. Currying - это процесс преобразования функции, принимающей несколько аргументов (a, b) -> c
в карриную форму a -> b -> c
, чтобы ее можно было частично применить.
Ответ 2
Вы можете заменить \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
Ответ 3
В этом конкретном случае вы можете использовать flip makeTuple2 n
, но это работает только для функций с двумя аргументами. Но в целом я не нахожу ваше решение с лямбдой un-haskelly или слишком pythonic.
Ответ 4
Если ваши функции являются только кортежами:
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)
В противном случае используйте уже предложенные способы.