Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

Ответ 2

То, что вы называете "doubleArgs", чаще всего называется dup - это комбинатор W (называемый warbler in To Mock пересмешник) - "элементарный дубликатор".

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

Haskell имеет довольно небольшую "комбинаторную основу", см. Data.Function, а также некоторые аппликативные и монадические операции добавляют больше "стандартных" комбинаторов в силу экземпляров функции для Applicative и Monad (< * > from Applicative - это S- скрининг-комбинатор для функционального экземпляра, liftA2 и liftM2 являются старловыми звездами). В сообществе нет большого энтузиазма для расширения Data.Function, поэтому, в то время как комбататоры - это очень весело, прагматично, я предпочитаю долгое время в ситуациях, когда комбинатор не доступен напрямую.

Ответ 3

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

import Control.Arrow

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

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