【问题标题】:Why the result of evaluating `init . cuts [1,2,3]` is different from `(init . cuts) [1,2,3]`?为什么评估 `init 的结果。 cut [1,2,3]` 与 `(init .cuts) [1,2,3]` 不同?
【发布时间】:2014-05-06 17:11:34
【问题描述】:

我试图理解为什么这两个评估:(init . cuts) [1,2,3]init . cuts [1,2,3] 不同,其中:

cuts :: [a] -> [([a],[a])]
cuts xs = zipWith splitAt [0..length xs] (repeat xs)

第一个给出了我期望的结果:[([],[1,2,3]),([1],[2,3]),([1,2],[3])] ,但第二个返回此错误:

<interactive>:389:8:
    Couldn't match expected type `a0 -> [a1]'
                with actual type `[([a2], [a2])]'
    In the return type of a call of `cuts'
    Probable cause: `cuts' is applied to too many arguments
    In the second argument of `(.)', namely `cuts [1, 2, 3]'
    In the expression: init . cuts [1, 2, 3]

我假设init . cuts [1,2,3] = init (cuts[1,2,3]),这是正确的吗?

谢谢,
塞巴斯蒂安。

【问题讨论】:

    标签: haskell types unification


    【解决方案1】:

    这是因为固定性或操作顺序。当编译器看到

    (init . cuts) [1, 2, 3]
    

    它将其解析为带有参数[1, 2, 3] 的函数inits . cuts。当它看到

    init . cuts [1, 2, 3]
    

    它将这个解析为函数inits与函数cuts [1, 2, 3]组成,但由于cuts [1, 2, 3]不是函数,它会引发类型错误。这是因为函数应用程序总是优先于运算符应用程序。这就是为什么你可以写出这样的表达式

    f = zipWith (+) [1..] . filter (< 10) . take 5
    

    而不是

    f = (zipWith (+) [1..]) . (filter (< 10)) . (take 5)
    

    如果运算符优先级可能高于函数应用程序优先级,那么您可能必须根据运算符优先级使用后一种形式,然后表达式变得更难编译器和程序员解析。这可能意味着像

    这样的东西可能有歧义
    x 1 <#> y
    

    因为它可以被解析为

    (x 1) <#> y
    

    或者

    x (1 <#> y)
    

    取决于优先级(我只是编了一个运算符&lt;#&gt;)。


    正如@chepner 正确指出的那样,如果您想避免使用括号(就像许多 Haskeller 所做的那样),您可以改用 $ 运算符,它具有精心挑选的固定性,可以让您这样做

    init . cuts $ [1, 2, 3]
    

    $ operator 的经验法则是它是相同的 具有相同的效果1 将出现在右侧的任何内容放在括号中,因此是一个复杂的表达式,例如

    func (func (func (func (func 1))))
    

    可以写成更好的形式

    func $ func $ func $ func $ func 1
    

    甚至

    func . func . func . func . func $ 1
    

    1 正如@Cubic 所指出的,有些人可能认为$ 是Haskell 的一些特殊语法。不是这种情况。它的定义非常简单

    ($) :: (a -> b) -> a -> b
    f $ x = f x
    infixr 0 $
    

    事实上,这个函数和id 函数是一样的,只是表示为一个中缀运算符,它与右边关联的优先级很低,而这种固定性使得该运算符对于非常清晰地表达代码很有用。它可以等同于使用更多的括号,但这并不能使其成为它们的替代语法。这只是另一个功能。

    【讨论】:

    • 可能值得注意的是,有一个低优先级函数应用运算符适用于这种情况:init . cuts $ [1,2,3],相当于(init . cuts) [1, 2, 3]
    • @chepner 已编辑以包含一些示例。
    • 另外,请参阅this answer 我不久前就同样的问题写过。
    • @Seba 也许我应该把它说成“把右边出现的任何东西,用括号括起来,然后把它传递给用括号括起来的左边的任何东西直到下一个@ 987654346@”。所以像f $ g . h $ 1 这样的东西会变成(f) ((g . h) (1)) === f ((g . h) 1)
    • @bheklilr 我仍然认为这是介绍$ 的糟糕方式。我看到不止一个人最终认为$ 是一种特殊语法,用于将表达式的部分字面意思包装在括号中。
    猜你喜欢
    • 2018-06-24
    • 2014-10-10
    • 2021-06-22
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 2017-05-03
    • 2014-10-14
    • 1970-01-01
    相关资源
    最近更新 更多