我不明白用arity编写函数> 1。 在ghci 7.4.1我打字:
((*).succ) 3 4
> 16
我不完全理解数学变换,但很明显它与
相同(*) (succ 3) 4
但是当我这样做时:
( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5
> 10
( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5
> No instance for (Num (a0 -> t0))
现在我完全迷失了。有人能解释会发生什么吗? PS。我知道haskell中的所有内容只有一个参数,但它并没有真正帮助我:)
答案 0 :(得分:12)
以这种方式解决:
(f . g) x = f (g x)
(f . g) x y = f (g x) y -- applying y
然后将{f替换为(*)
,将g替换为succ
,将x和y替换为其值:
((*) . succ) 3 4 = (*) (succ 3) 4
= (*) 4 4
= 16
答案 1 :(得分:5)
撰写(\x y z -> x) . (\a b -> a*b)
时,您将撰写以下签名的功能:
(\x y z -> x) :: a -> b -> c -> a
(\a b -> a*b) :: Num a => a -> a -> a
(.)
的签名是
(.) :: (b -> c) -> (a -> b) -> a -> c
现在,让我们把事情放在一起,为这些功能获得(.)
的特殊签名版本。
首先,我们将(b -> c)
签名的(.)
部分专门化为a -> b -> c -> a
:
(b -> (z -> x -> b)) -> (a -> b) -> a -> (z -> x -> b)
得到它? c
变为(z -> x -> b)
。
现在让我们将(a -> b)
签名的(.)
部分专门化为a -> a -> a
:
((a -> a) -> (z -> x -> (a -> a))) -> (a -> (a -> a)) -> a -> (z -> x -> (a -> a))
b
变为(a -> a)
。
现在让我们删除多余的大括号:
((a -> a) -> z -> x -> a -> a) -> (a -> a -> a) -> a -> z -> x -> a -> a
现在来自ghci的会话显示签名如何更改,因此我将所有参数应用于该签名的函数:
> let f = undefined :: ((a -> a) -> z -> x -> a -> a) -> (a -> a -> a) -> a -> z -> x -> a -> a
> :t f (\x y z -> x)
f (\x y z -> x) :: (a -> a -> a) -> a -> z -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b)
f (\x y z -> x) (\a b -> a*b) :: Num a => a -> z -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2
f (\x y z -> x) (\a b -> a*b) 2 :: Num a => z -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2 3
f (\x y z -> x) (\a b -> a*b) 2 3 :: Num a => x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2 3 4
f (\x y z -> x) (\a b -> a*b) 2 3 4 :: Num a => a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2 3 4 5
f (\x y z -> x) (\a b -> a*b) 2 3 4 5 :: Num a => a
以上解释了( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5
的工作原理。
现在,( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5
翻译的方式如下:
((a -> a) -> z -> x -> z) -> (a -> a -> a) -> a -> z -> x -> z
以下是会议结果:
> let f = undefined :: ((a -> a) -> z -> x -> z) -> (a -> a -> a) -> a -> z -> x -> z
> :t f (\x y z -> x)
f (\x y z -> x) :: (a -> a -> a) -> a -> (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b)
f (\x y z -> x) (\a b -> a*b)
:: Num a => a -> (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2
f (\x y z -> x) (\a b -> a*b) 2 :: Num a => (a -> a) -> x -> a -> a
> :t f (\x y z -> x) (\a b -> a*b) 2 3
f (\x y z -> x) (\a b -> a*b) 2 3
:: (Num a, Num (a -> a)) => x -> a -> a
最后一行解释了您的错误消息。 Num
显然不能有任何a -> a
个实例。
答案 2 :(得分:3)
既然你明白一切都是单参数函数,那就让我们从这一点开始吧。请记住(\ xyz - > x)确实是(\ x - >(\ yz - > x)),而这又是(\ x - >(\ y - >(\ z) - > x))),但让我们在第一步停止,以保持括号内的噪音。
(f . g) x = f (g x)
因此
((\x -> (\y z -> x)) . (\a b -> a*b)) 2 =
(\x -> (\y z -> x)) ((\a -> (\b -> a*b)) 2) =
(\x -> (\y z -> x)) (\b -> 2*b) =
(\y z -> (\b -> 2*b))
现在记住第二步并展开(\ y z - > ...):
(\y z -> (\b -> 2*b)) 3 4 =
(\y -> (\z -> (\b -> 2*b))) 3 4 =
-- \y -> ... given anything, returns a function \z -> ...
(\z -> (\b -> 2*b)) 4 =
-- \z -> ... given anything, returns a function \b -> ...
(\b -> 2*b)
最终是:
(\b -> 2*b) 5 = 2*5 = 10
如果第一个函数返回y而不是x:
,则故事展开的方式不同((\x -> (\y z -> y)) . (\a -> (\b -> a*b))) 2 =
(\x -> (\y z -> y)) ((\a -> (\b -> a*b)) 2) =
(\x -> (\y z -> y)) (\b -> 2*b) =
-- \x -> ... given anything, returns a function \y z -> ...
(\y z -> y)
所以你得到:
(\y -> (\z -> y)) 3 4 5 =
-- \y -> ... given anything, returns a function \z -> ...
(\z -> 3) 4 5 =
-- \z -> ... given anything, returns a constant 3
3 5 -- now still trying to apply 5 to 3
它正在尝试将3
视为可以采用5
的函数。
答案 3 :(得分:2)
组合器.
是function composition运算符。让我们来看一下它的类型:
(.) :: (b -> c) -> (a -> b) -> a -> c
因此它接受第二个函数的结果并将其传递给第一个函数。
在您的示例中,需要考虑的重要事项是我们可以将*
视为单个argmuent函数,其结果是函数:
(*) :: Num a => a -> (a -> a)
它接受一个数字并返回一个函数,该函数将其参数乘以数字。 (这种方法称为Currying)。类型运算符->
与右侧相关联,因此括号是可选的 - 它只是在我们的脑海中,如果我们将(*)
视为一个双参数函数返回一个数字或一个参数函数返回另一个功能
这有助于我们查看(*) . succ
的作用:它递增其参数(必须是Enum
),然后将其转换为一个函数,该函数将其参数乘以数字(因此参数也必须是Num
)。结果是
(*) . succ :: (Enum a, Num a) => a -> (a -> a)
同样,我们可以将它视为单参数函数,或者更方便的两参数函数:它递增第一个参数并将其与第二个参数相乘。
答案 4 :(得分:0)
在
( (\x y z -> x).(\a b -> a*b) ) 2 3 4 5
但在
( (\x y z -> y).(\a b -> a*b) ) 2 3 4 5
第二次评估将导致此((\ b - > 2 * b)3 4 - > 3)剩余3 5导致错误