【问题标题】:Haskell: Using map in function compositionHaskell:在函数组合中使用映射
【发布时间】:2012-05-07 21:47:44
【问题描述】:

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

任何建议将不胜感激。

【问题讨论】:

  • 回答this question 有帮助吗? (特别是this one
  • 第一个实际产生输出Couldn't match expected type a0 -> [b0]' with actual type [a1]'

标签: haskell syntax map function-composition


【解决方案1】:

回忆(.)的类型。

(.) :: (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),其中bcNum 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]

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

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

甚至:

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

【讨论】:

  • 我知道 OP 专门询问了函数组合,但这个例子真的最适合直接$map (*2) $ filter even [1,2,3,4]
【解决方案2】:

以下表格有效:

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】:

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

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

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

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

    因为它组合了两个函数并将结果函数应用于参数。

    【讨论】:

      【解决方案4】:

      由于(.)的低优先级,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]
      

      【讨论】:

        【解决方案5】:

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

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

        现在,您已经通过传递参数(*2) 将a 函数提供到b 中。因此,您部分应用的 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) 所期望的签名不同。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-22
          相关资源
          最近更新 更多