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

StackOverflow https://stackoverflow.com/questions/4333864

  •  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 В конце, чтобы подавать пару в функции, ожидая два аргумента.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top