the*_*sti 6 haskell function composition
我想组合加法运算符(+)来创建这种类型的函数:
Num a => a -> a -> a -> a
Run Code Online (Sandbox Code Playgroud)
就像,相当于:
(\a b c -> a + b + c)
Run Code Online (Sandbox Code Playgroud)
但不必诉诸lambdas.
我已经试过了
((+) . (+))
Run Code Online (Sandbox Code Playgroud)
我原本希望工作,但令人惊讶的是没有.
che*_*ner 11
http://pointfree.io给出了\a b c -> a + b + cas 的免费版本((+) .) . (+).
非正式地,组合仅对"一阶函数""直观地"工作,它既不将函数作为参数也不将函数作为值返回.(+)是一个高阶函数; 它接受一个类型的值Num a => a,并返回一个类型的函数Num a => a -> a.当您尝试以天真的方式编写高阶函数时,结果不是您所期望的:
:t (+) . (+)
(+) . (+) :: (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)
考虑两个函数的定义:
(+) :: Num z => z -> z -> z
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)
Run Code Online (Sandbox Code Playgroud)
然后
(+) . (+) == (.) (+) (+)
== \x -> (+) ((+) x)
Run Code Online (Sandbox Code Playgroud)
因为currying,你最终会传递一个函数,而不是一个数字,作为第一个参数(+).
那么,我们如何从中获取h a b c = a + b + c到h = ((+) .) . (+)?首先使用(+)左关联的事实将中缀表达式重写为前缀表达式.
\a b c -> a + b + c
== \a b c -> ((+) a b ) + c
== \a b c -> (+) ((+) a b) c
Run Code Online (Sandbox Code Playgroud)
接下来,我们交替应用eta转换来消除参数和组合以将参数移动到要消除的位置.我试图非常明确地确定用于组合应用的函数.
== \a b -> (+) ((+) a b) -- eta conversion to eliminate c
== \a b -> (+) (((+) a) b) -- parentheses justified by currying
-- f g -- f = (+), g = ((+) a)
-- \a b -> f ( g b)
-- \a b -> (f . g) b -- definition of (.)
== \a b -> ((+) . ((+) a)) b
== \a -> (+) . ((+) a) -- eta conversion to eliminate b
== \a -> (.) (+) ((+) a) -- prefix notation
== \a -> ((.) (+)) ((+) a) -- parentheses justified by currying
== \a -> ((+) . )((+) a) -- back to a section of (.)
-- f g -- f = ((+) .), g = (+)
-- \a -> f (g a)
-- \a -> ( f . g) a -- definition of (.)
== \a -> (((+) .) . (+)) a
== ((+) .) . (+) -- eta conversion to eliminate a
Run Code Online (Sandbox Code Playgroud)
你需要这个奇怪的运算符(.).(.),有时定义为.:(想想3个点...)
在ghci
Prelude> let (.:) = (.).(.)
Prelude> let f = (+) .: (+)
Prelude> f 1 2 3
> 6
Run Code Online (Sandbox Code Playgroud)
请注意,此运算符也可以定义为<$$> = fmap . fmap.
虽然这会引入一些噪音,但您可以使用uncurry :: (a -> b -> c) -> (a,b) -> c和curry :: ((a,b) -> c) -> a -> b -> c临时存储第二个加号的参数在一个元组中:
curry $ (+) . uncurry (+) :: Num a => a -> a -> a -> a
Run Code Online (Sandbox Code Playgroud)
或者更具语义可读性:
curry ((+) . uncurry (+)) :: Num a => a -> a -> a -> a
Run Code Online (Sandbox Code Playgroud)
uncurry因此需要一个函数(这里(+))并将其转换为函数:uncurry (+) :: Num a => (a,a) -> a.因此,您已将其转换(+)为带有元组的函数.
现在我们可以使用(.)第一个制作合成(+):
(+) . uncurry (+) :: Num a => (a,a) -> (a -> a)
Run Code Online (Sandbox Code Playgroud)
所以现在我们有一个函数,它接受一个参数(元组(a,a))并产生一个函数,它接受a(第一个的第二个操作数(+))并计算总和.问题当然是我们要摆脱元组.我们可以通过将函数传递给a来实现curry.这将tuple-function((a,a) -> (a -> a))转换为一个单独获取参数的函数(a -> (a -> (a -> a))).