Трюк для повторного использования аргументов в Haskell?

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

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


person Landei    schedule 02.12.2010    source источник


Ответы (3)


От 1_:

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

person luqui    schedule 02.12.2010
comment
И согласно pointfree, dup получается liftM2. Мне действительно нужно лучше разобраться с экземпляром монады для функций. - person Antal Spector-Zabusky; 02.12.2010
comment
Спасибо вам обоим за предоставление даже двух подходов к решению таких проблем. Кстати, я попробовал sqr = (*) <$> id <*> id, и он тоже работает :-) - person Landei; 02.12.2010
comment
a ++ (x:b) на 3 символа короче, чем ваш вариант, может быть, поэтому некоторые люди предпочитают его? - person John L; 02.12.2010
comment
Если бы я хотел подчеркнуть симметрию, я бы лучше написал concat [a,[x],b] вместо a ++ [x] ++ b - person Landei; 02.12.2010
comment
@Antal S-Z: На самом деле это не так уж и много - просто легкая монада Reader, которую легко использовать встроенной. Первый аргумент служит средой, fmap и return не зависят от среды, как и следовало ожидать, и т. д. Одно из моих любимых применений — это условный комбинатор (<?>), который можно использовать как even <?> (`div` 2) <*> (+ 1), который, как мне кажется, намного читабельнее, чем \n -> if even n then n div. ` 2 else n + 1. (n.b. -- liftM2 (\bte -> if b then t else e)` вызовет побочные эффекты от обеих ветвей, хотя это не относится к Reader) - person C. A. McCann; 02.12.2010
comment
Этот ответ помог мне интуитивно понять, что join делает с функцией. Спасибо. - person N3dst4; 18.10.2017

То, что вы называете «doubleArgs», чаще называют dup — это комбинатор W (называемый камышевкой в ​​«Издеваться над пересмешником») — «элементарный дубликатор».

То, что вы называете «дублированием», на самом деле является комбинатором «старлинг-прайм».

Haskell имеет довольно небольшую «основу комбинатора», см. Data.Function, плюс некоторые аппликативные и монадические операции добавляют больше «стандартных» комбинаторов благодаря экземплярам функций для аппликативных и монадных (‹*> от Applicative — это комбинатор S-starling для функционального экземпляра, liftA2 и liftM2 являются первичными). Кажется, в сообществе нет большого энтузиазма по поводу расширения Data.Function, поэтому, хотя комбинаторы — это весело, прагматически я предпочитаю длинную руку в ситуациях, когда комбинатор недоступен напрямую.

person stephen tetley    schedule 02.12.2010
comment
О, я нашел птиц-операторов для Haskell: hackage.haskell.org/packages/archive/data-aviary/0.2.3/doc/html/ - person Landei; 02.12.2010
comment
@Landei - я считаю их только справочными, т. Е. Я бы не рекомендовал полагаться на них в рабочем коде. Я должен сделать описание Кабалы более ясным, чтобы оно было только ссылкой, но я еще не дошел до этого. - person stephen tetley; 02.12.2010
comment
То, что @Landei называет dup, также известно как разветвление глагола в J, где это написано простым сопоставлением операторов, например (f g h) x вместо dup f g h x. - person C. A. McCann; 02.12.2010

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

import Control.Arrow

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

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

person Landei    schedule 25.05.2011