6

我对 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]'

任何建议将不胜感激。

4

5 回答 5

18

回想 的类型(.)

(.) :: (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)、位置bc位置Num a => [a]。但是,第二个参数是一个列表:

Prelude> :t filter even [1,2,3,4]
filter even [1,2,3,4] :: Integral a => [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]

或使用显式括号,删除两个函数的组合

map (*2) (filter even [1,2,3,4])

甚至:

(map (*2) . filter even) [1,2,3,4]
于 2012-05-06T14:06:21.740 回答
5

的结果zipWith max [1,2] [4,5]是一个列表,而不是一个函数。(.) 运算符需要一个函数作为其右操作数。因此,您的第二行出现错误。可能你想要的是

map (*2) (zipWith max [1,2] [4,5])

您的第一个示例无法在 WinHugs(Hugs 模式)上编译;它有同样的错误。以下将起作用

(map (*2) . filter even) [1,2,3,4]

因为它由两个函数组成,并将结果函数应用于一个参数。

于 2012-05-06T13:58:32.207 回答
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]在这种非多态的情况下,所以这是错误类型的。另一方面($)有 type (a -> b) -> a -> b,或者([Int] -> [Int]) -> [Int] -> [Int]在这种非多态的情况下,所以这个:

(map (* 2)) $ (((zipWith max) [1, 2]) [4, 5])

是很好的类型。

于 2012-05-06T14:07:47.967 回答
2

由于 的优先级较低(.),Haskell 解析

map (*2) . filter even [1,2,3,4]

作为

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]
于 2012-05-06T14:08:53.467 回答
2

如果您检查地图的类型,它是:(a -> b) -> [a] -> [b]

因此,它将 a 的函数放入 b 中,然后将 a 的列表放入并返回 b 的列表。正确的?

现在,您已经通过传递参数将 a 函数提供到 b 中(*2)。因此,您部分应用的 map 函数最终是:[Integer] -> [Integer]这意味着您将收到一个整数列表并返回一个整数列表。

到目前为止,您可以编写 (.) 一个具有相同签名的函数。如果您检查什么是类型,filter even您会看到它是:[Integer] -> [Integer],因此是此处作文的有效候选者。

那么这个组合不会改变函数的最终签名,如果你检查类型:map (*2) . filter even它是[Integer] -> [Integer]

情况并非如此,map (*2) . zipWith max [1,2] [4,5]因为 与zipWith max预期的签名不同map (*2)

于 2012-05-06T14:08:59.567 回答