Трюк с «повторным использованием» аргументов в Haskell - PullRequest
20 голосов
/ 02 декабря 2010

Время от времени я сталкиваюсь с проблемой, которую хочу выразить «пожалуйста, дважды используйте последний аргумент», например, для того, чтобы написать стиль без смысла или избежать лямбды. Э.Г.

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 в Hoogle, я думаю, что здесь я мог бы пропустить трюк или идиому.

Ответы [ 3 ]

29 голосов
/ 02 декабря 2010

С 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 ... это не быстрее -- об этом позаботится лайнер - а последний намного более симметричен! О, хорошо)

11 голосов
/ 02 декабря 2010

То, что вы называете «doubleArgs», чаще всего называется dup - это W-комбинатор (называемый warbler в «Mock a Mockingbird») - «элементарный дубликатор».

То, что вы называете «dup», на самом деле является комбинатором «starling-prime».

Haskell имеет довольно небольшую «основу комбинатора», см. Data.Function, а также некоторые операции Applicative и Monadic добавляют дополнительные «стандартные» комбинаторы благодаря экземплярам функций для Applicative и Monad (<*> от Applicative являетсяS - комбинатор скворец для функционального экземпляра, ЛифтА2 и ЛифтМ2 являются первичными).Похоже, что в сообществе не так много энтузиазма по поводу расширения Data.Function, так что, хотя комбинаторы очень забавны, прагматично, но я предпочел длинную руку в ситуациях, когда комбинатор не доступен напрямую.

9 голосов
/ 25 мая 2011

Вот еще одно решение для второй части моего вопроса: стрелки!

import Control.Arrow

ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))

&&& ("разветвление") распределяет аргумент по двум функциям и возвращает пару результатов.>>> («а потом») меняет порядок применения функции, что позволяет иметь цепочку операций слева направо.second работает только на второй части пары.Конечно, вам нужно uncurry в конце, чтобы передать пару в функцию, ожидающую два аргумента.

...