要从GHCi中的列表中删除最后一项,我可以反转列表,取尾然后再将其反转。例如,
reverse(tail(reverse([1,2,3,4])))
由于那里有很多括号,我以为我会改变它来使用功能组合。但是,当我尝试这个时,我得到以下错误。
Prelude> reverse . tail. reverse [1,2,3,4]
<interactive>:2:17:
Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
In the return type of a call of `reverse'
Probable cause: `reverse' is applied to too many arguments
In the second argument of `(.)', namely `reverse [1, 2, 3, 4]'
In the second argument of `(.)', namely
`tail . reverse [1, 2, 3, 4]'
我认为这意味着它不喜欢编写reverse [1,2,3,4]
,所以我尝试在它周围加上括号,但它给了我同样的错误。
Prelude> reverse . tail. (reverse [1,2,3,4])
<interactive>:3:18:
Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
In the return type of a call of `reverse'
Probable cause: `reverse' is applied to too many arguments
In the second argument of `(.)', namely `(reverse [1, 2, 3, 4])'
In the second argument of `(.)', namely
`tail . (reverse [1, 2, 3, 4])'
但是,如果我执行以下操作,它可以正常工作。
Prelude> let f = reverse . tail . reverse
Prelude> f [1,2,3,4]
[1,2,3]
导致此错误的原因是什么?为什么let绑定会阻止此错误发生?
答案 0 :(得分:8)
当你看到.
的类型时,你会注意到它的两个操作数都是函数(合适的类型,以便它们可以组合)
Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base'
infixr 9 .
现在,函数应用程序的优先级高于任何中缀运算符,所以
Prelude> reverse . tail . reverse [1,2,3,4]
成为
Prelude> reverse . tail . (reverse [1,2,3,4])
当您尝试应用合成时,其类型错误。要使组合更正,你必须明确给出比函数应用程序更高的优先级,你可以通过提供明确的括号来完成。
Prelude> (reverse . tail . reverse) [1,2,3,4]
此解决方案有效,但是黑客们讨厌使用括号。这里有另一个运算符$
,它可以将您保存在这些位置,并使您的代码比使用括号更具可读性。
当您看到$
Prelude> :i ($)
($) :: (a -> b) -> a -> b -- Defined in `GHC.Base'
infixr 0 $
您可以清楚地看到它的优先级(0)小于构图(9)的优先级,它可以为您提供所需的效果。您只需降低上一个函数应用程序的优先级或用$
替换函数组合。
Prelude> reverse . tail . reverse $ [1,2,3,4]
Prelude> reverse . tail $ reverse [1,2,3,4]
Prelude> reverse $ tail $ reverse [1,2,3,4]
这很完美,因为$
的第一个操作数是一个函数而另一个只是一个正确类型的值。
答案 1 :(得分:4)
在let
情况下,函数由.
组成,然后列表作为函数组合结果的参数:
(reverse . tail . reverse) [1,2,3,4]
在另一种情况下,列表作为reverse
的参数给出,并且其结果试图与其他一些函数组合:
reverse . tail . (reverse [1,2,3,4])
由于reverse [1,2,3,4]
返回的反向列表不是函数,因此将其用作.
的参数会导致类型错误。