我对Haskell相对较新,如果我的问题听起来很愚蠢,那么道歉。我一直试图理解功能组合是如何工作的,我遇到了一个问题,我想知道有人可以帮助我。我在以下两个场景中使用函数组合中的map:
map (*2) . filter even [1,2,3,4]
map (*2) . zipWith max [1,2] [4,5]
尽管filter和zipWith函数都返回一个列表,但只有第一个组合有效,而第二个组合会抛出以下错误:
"Couldn't match expected type '[Int] -> [Int]' with actual type '[c0]'
任何建议都将不胜感激。
答案 0 :(得分:17)
回想(.)
。
(.) :: (b -> c) -> (a -> b) -> a -> c
它有三个参数:两个函数和一个初始值,并返回两个函数的结果。
现在,函数对其参数的应用比(.)
运算符更紧密。
所以你的表达:
map (*2) . filter even [1,2,3,4]
被解析为:
(.) (map (*2)) (filter even [1,2,3,4])
现在,第一个参数map (*2)
没问题。它的类型为(b -> c)
,其中b
和c
为Num a => [a]
。但是,第二个参数是一个列表:
Prelude> :t filter even [1,2,3,4]
filter even [1,2,3,4] :: Integral a => [a]
所以当[a]
函数需要函数时,类型检查器会抱怨你传递(.)
作为参数。
这就是我们所看到的:
Couldn't match expected type `a0 -> [b0]' with actual type `[a1]'
In the return type of a call of `filter'
In the second argument of `(.)', namely `filter even [1, 2, 3, 4]'
In the expression: map (* 2) . filter even [1, 2, 3, 4]
所以......括号!
使用$
运算符添加括号:
map (*2) . filter even $ [1,2,3,4]
或使用显式的parens,删除两个函数的组合
map (*2) (filter even [1,2,3,4])
甚至:
(map (*2) . filter even) [1,2,3,4]
答案 1 :(得分:5)
zipWith max [1,2] [4,5]
的结果是列表,而不是函数。 (。)运算符需要一个函数作为其右操作数。因此你的第二行错误。可能你想要的是
map (*2) (zipWith max [1,2] [4,5])
你的第一个例子不能在WinHugs上编译(拥抱模式);它有同样的错误。以下将起作用
(map (*2) . filter even) [1,2,3,4]
因为它组成了两个函数并将结果函数应用于参数。
答案 2 :(得分:5)
以下表格有效:
map (* 2) $ filter even [1, 2, 3, 4]
(map (* 2) . filter even) [1, 2, 3, 4]
map (* 2) $ zipWith max [1, 2] [4, 5]
(\xs -> map (* 2) . zipWith max xs) [1, 2] [4, 5]
但不是以下内容:
map (* 2) . filter even [1, 2, 3, 4]
map (* 2) . zipWith max [1, 2] [4, 5]
(map (* 2) . zipWith max) [1, 2] [4, 5]
为什么会这样?好吧,举个例子
map (* 2) . zipWith max [1, 2] [4, 5]
与
相同(map (* 2)) . (((zipWith max) [1, 2]) [4, 5])
(map (* 2))
的类型为[Int] -> [Int]
(假设默认为Int
),(((zipWith max) [1, 2]) [4, 5])
的类型为[Int]
,(.)
的类型为(b -> c) -> (a -> b) -> a -> c
在这种非多态的情况下,或([Int] -> [Int]) -> ([Int] -> [Int]) -> [Int] -> [Int]
,所以这是错误的类型。另一方面,($)
在此非多态情况下具有(a -> b) -> a -> b
或([Int] -> [Int]) -> [Int] -> [Int]
类型,因此:
(map (* 2)) $ (((zipWith max) [1, 2]) [4, 5])
打字很好。
答案 3 :(得分:2)
由于(.)
的优先级较低,Haskell解析
map (*2) . filter even [1,2,3,4]
as
map (*2) . (filter even [1,2,3,4])
即。用map (*2)
(一个列表)的结果组合filter even [1,2,3,4]
(一个函数),这是没有意义的,并且是一个类型错误。
您可以使用@ Theodore的建议或使用($)
:
map (*2) . filter even $ [1,2,3,4]
答案 4 :(得分:2)
如果您检查地图类型:(a -> b) -> [a] -> [b]
因此,它需要a的函数进入b然后是a的列表并返回b的列表。正确?
现在,您已经通过传递参数(*2)
提供了a到b的函数。因此,部分应用的地图函数最终为:[Integer] -> [Integer]
意味着您将收到一个整数列表并返回一个整数列表。
到目前为止,您可以撰写(。)具有相同签名的函数。如果你检查filter even
的类型是什么,你会看到它是:[Integer] -> [Integer]
,这里是一个有效的合成候选者。
如果您检查类型:map (*2) . filter even
它是[Integer] -> [Integer]
map (*2) . zipWith max [1,2] [4,5]
不会出现这种情况,因为zipWith max
与map (*2)
所期望的签名不同。