Трюк для «повторного использования» аргументов в Хаскелле?
-
30-09-2019 - |
Вопрос
Время от времени я наткнулся на проблему, которую я хочу выразить: «Пожалуйста, используйте последний аргумент дважды», например, чтобы написать стиль без точки или избежать лямбды. Например
sqr x = x * x
может быть написано как
sqr = doubleArgs (*) where
doubleArgs f x = f x x
Или считать эту немного более сложную функцию (взятая из этот вопрос):
ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)
Я мог бы написать этот кодовый точка, если бы была такая функция:
ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
dup f f1 f2 x = f (f1 x) (f2 x)
Но так как я не могу найти что -то вроде DoubleArgs или Dup в Google, поэтому я думаю, что я могу пропустить трюк или идиому здесь.
Решение
От Control.Monad
:
join :: (Monad m) -> m (m a) -> m a
join m = m >>= id
instance Monad ((->) r) where
return = const
m >>= f = \x -> f (m x) x
Расширение:
join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
= \x -> id (f x) x
= \x -> f x x
Так что да, Control.Monad.join
.
О, и для вашего примера, если бы вы ни были Control.Applicative
):
ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails
(Я также не знаю, почему люди так любят a ++ (x:b)
вместо a ++ [x] ++ b
... Это не быстрее - инлушитель позаботится об этом - и последний намного более симметричен! Ну что ж)
Другие советы
То, что вы называете «DoubleArgs», чаще называют DUP - это комбинатор W (называемый камышкой, чтобы издеваться над пересмешником) - «Элементарный дубликатор».
То, что вы называете «Dup», на самом деле комбинатор «Starling-Prime».
Haskell имеет довольно небольшую «базу комбинатора», см. Function, плюс некоторые применяющие и монадические операции добавляют больше «стандартных» комбинаторов в силу экземпляров функции для Applicative и Monad (<*> из Applications составляет S -Starling Combinator для Функциональный экземпляр, Lifta2 и LiftM2-Starling-Prime). Похоже, что в сообществе нет большого энтузиазма по поводу расширения данных. Поэтому, хотя комбинаторы-это весело, прагматично я предпочитал долгое русло в ситуациях, когда комбинатор недоступен непосредственно.
Вот еще одно решение для второй части моего вопроса: стрелы!
import Control.Arrow
ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))
То &&&
(«Fanout») распределяет аргумент на две функции и возвращает пару результатов. >>>
(«А затем») меняет порядок применения функции, который позволяет иметь цепочку операций слева направо. second
работает только во второй части пары. Конечно вам нужен uncurry
В конце, чтобы подавать пару в функции, ожидая два аргумента.