"加"运算符加入一个接受3个参数的函数?

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 + ch = ((+) .) . (+)?首先使用(+)左关联的事实将中缀表达式重写为前缀表达式.

\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)


mb1*_*b14 8

你需要这个奇怪的运算符(.).(.),有时定义为.:(想想3个点...)

在ghci

Prelude> let (.:) = (.).(.)
Prelude> let f = (+) .: (+) 
Prelude> f 1 2 3 
> 6
Run Code Online (Sandbox Code Playgroud)

请注意,此运算符也可以定义为<$$> = fmap . fmap.


Wil*_*sem 6

虽然这会引入一些噪音,但您可以使用uncurry :: (a -> b -> c) -> (a,b) -> ccurry :: ((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))).