Есть ли имя для функции, которая берет часть данных и список функций и применяет каждую функцию к результату последней? - PullRequest
13 голосов
/ 03 марта 2012

У Clojure есть макрос, ->, который берет часть данных и набор функций, применяет данные к первой функции, а затем применяет результат этого к следующей, результат что третьему и т. д., наконец, возвращая вам результат последнего приложения.

Мне это очень нравится, потому что вместо того, чтобы писать функции в обратном порядке, в порядке их применениявот так: (псевдокод следует)

floor (square.root (x))

вы можете записать их в том порядке, в котором данные проходят через них:

-> x (square.root, floor)

У меня вопрос, есть ли стандарт?имя для этой функции в функциональных языках, в том смысле, что карты, сокращения и фильтры имеют стандартные имена?Документы Clojure описывают это как «потоковую передачу данных через функции», но я не смог найти ничего по поиску слова «1012 *».Я написал простую версию в Haskell:

thread :: a -> [(a -> a)] -> a
thread x []     = x
thread x (f:fs) = thread (f x) fs

и искал a -> [(a -> a)] -> a в Hoogle, но это тоже ничего не придумало.

При поиске этого вопросаЯ также заметил, что вы можете сделать очень похожую вещь, используя операторы композиции функций из Control.Arrow в Haskell, например:

($2) (sin >>> cos >>> tan)

, тогда как при использовании выделенной функции thread высшего порядка вы должны написать:

thread 2 [sin, cos, tan]

Возможно ли, что первой формулировки достаточно для практического использования?

Ответы [ 6 ]

7 голосов
/ 03 марта 2012

Имя, которое вы ищете: композиция функций .

Разница между макросом потоков и comp обусловлена ​​первым, использующим гомойконичность Clojure, в то время как последний более близок к математическому определению композиции.

Фактически, вы можете преобразовать вызов макропотокового потока в вызов comp, если вы создадите одну функцию с одним аргументом на шаг и измените порядок функций: * * 1010

4 голосов
/ 03 марта 2012

Я думаю, вы можете использовать foldl для этого, используя соответствующие аргументы:

-- functions for testing
f1 x = x+1
f2 x = x*2
f3 x = x*x
-- here comes the line you're interested in
foldl (\x y -> y x) 2 [f1, f2, f3]

Аргументы таковы:

  • (\x y -> y x) - это функция, принимающая функцию y и применяющая ее к аргументу x. y будет затем заменен каждой функцией из списка.
  • 2 - это начальный аргумент, который вы хотите дать функциям.
  • [f1, f2, f3] - список функций.

Используя эти аргументы, foldl вычисляет следующее: f3(f2(f1(2))).

2 голосов
/ 04 марта 2012

Это не вполне функция, которую вы ищете, но для этой функции есть «идиоматическое имя»:

foldr (.) id

Опытному Хаскеллеру даже не нужноподумайте, что это значит;это достаточно часто, чтобы быть одним словом.

foldr (.) id :: [a -> a] -> a -> a
foldr (.) id [abs, succ, negate] 42
    = (abs . succ . negate . id) 42
    = abs (succ (negate 42))

Функция, которую вы запрашивали, для каждой функции, получающей вывод предыдущего , а не следующего , будет

foldr (.) id . reverse

Но если вы можете управлять им, используйте прежнюю версию (например, она более ленивая, т. Е. Может выдавать результат при бесконечном списке функций).

2 голосов
/ 03 марта 2012

Учитывая, что >>> похож на оператор конвейера оболочки bash с корнем из Unix , я бы назвал это конвейерная последовательность комбинатор.

0 голосов
/ 13 марта 2012

Оливер Стил называет это sequence в своей функциональной библиотеке Javascript. pipe тоже может быть хорошим именем.

0 голосов
/ 03 марта 2012

Вы не можете иметь такую ​​функцию в haskell, потому что она будет ограничена только функциями, которые принимают и возвращают один и тот же тип.И это не очень полезно.

Но вы можете использовать либо

состав функции:

floor . sqrt 

, либо стрелки:

sqrt >>> floor
...