【问题标题】:What is $ in Haskell, and how to apply functions to functions [duplicate]Haskell中的$是什么,以及如何将函数应用于函数[重复]
【发布时间】:2018-11-02 15:34:31
【问题描述】:

我正在使用 Haskell 进行 Project Euler,并在完成 the very first problem 时发现了一些要学习的东西。这是我的代码:

isValid x = (mod x 3 == 0) || (mod x 5 == 0)
listValid :: Integer -> [Integer]
listValid n = [x | x <- [1..n-1], isValid x]

函数listValid 将获得小于n 且可被3 或5 整除的所有正整数。很简单。

*Main> listValid 10
[3,5,6,9]

现在我需要对它们求和。我认为 sum 函数是正确的方法。我不明白为什么前两个版本可以工作,而第三个版本不行。

*Main> sum (listValid 10)
23
*Main> sum $ listValid 10
23
*Main> sum listValid 10

<interactive>:4:5:
    Couldn't match type ‘[Integer]’ with ‘a0 -> t’
    Expected type: Integer -> a0 -> t
      Actual type: Integer -> [Integer]
    Relevant bindings include it :: t (bound at <interactive>:4:1)
    In the first argument of ‘sum’, namely ‘listValid’
    In the expression: sum listValid 10

这是一个操作顺序问题,我需要用括号括起来以断言应该首先应用哪个函数?如果是这样,$ 在第二个版本中做了什么?

【问题讨论】:

  • “这是一个运算顺序问题,我需要用括号括起来以断言应该首先应用哪个函数?” 是的。 “如果是这样,$ 在第二个版本中做了什么?” 就是这样。 :)

标签: function haskell operator-precedence


【解决方案1】:

这是关于关联性。函数应用是左关联的,所以sum listValid 10 等价于(sum listValid) 10,而不是sum (listValid 10)。如果你仔细想想,它必须是这样的:如果你定义add x y = x+y,你不会希望add 1 2 等同于add (1 2)

所以这里的问题是在sum listValid 10 中,它没有将listValid 10 作为sum 的参数;它将listValid 视为sum 的参数,然后将10 视为sum listValid 的参数。

$ 解决了这个问题,因为它是一个中缀运算符,很明显sum 是它的左操作数,listValid 10 是它的右操作数(请记住,函数应用程序的优先级高于任何中缀运算符,所以不能被视为(sum $ listValid) 10)。

【讨论】:

  • 它更多的是关于优先级而不是关联性,或者sum $ listValid 10 在将结果应用于10 之前仍会尝试评估sum $ listValid。 (另外,只有当多个$ 运算符同时使用时,右关联才有意义;f $ g $ hf $ (g $ h) 相同,而不是(f $ g) $ h)。
  • 由于f g x关联性 问题,优先级f $ g x 中发挥作用。
【解决方案2】:

函数应用f x是最高优先级的操作(并且是左关联的),所以

sum listValid 10

相当于(sum listValid) 10

另一方面,$ 运算符具有最低的优先级(并且是右关联的,尽管这与此处无关),因此

sum $ listValid 10

隐含地与sum $ (listValid 10) 相同,而不是(sum $ listValid) 10。因此,它通常用于消除括号。

【讨论】:

    【解决方案3】:

    当你写f $ x时,实际上你写的是($) f x($) :: (a -&gt; b) -&gt; a -&gt; b是一个函数。这个函数是defined as

    ($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
    f $ x = f x
    

    以上看起来并不令人印象深刻。如果你这样写f $ x,它就相当于f x,那为什么还要写$呢?因为这个运算符有precedence 0。因此,这意味着如果你写:

    f $ x+2
    

    它被解释为:

    ($) f (x+2)
    

    因此:

    f (x+2)
    

    不需要写括号。

    回到你的问题,如果你写:

    sum $ listValid 10
    

    这被解析为:

    ($) (sum) (listValid 10)
    

    因此在功能上等同于:

    sum (listValid 10)
    

    如果你写:

    sum listValid 10
    

    Haskell 将其解释为:

    (sum listValid) 10
    

    现在Integer -&gt; [Integer] 类型的函数的sum 没有意义,sum :: Num a =&gt; [a] -&gt; a 应该采用Numerical 值的列表,因此会出现错误。

    【讨论】:

      【解决方案4】:

      函数应用是左关联的,所以

      f x y
      

      被解析为:

      (f x) y
      

      但是,函数应用的优先级高于任何中缀运算符,所以

      f x $ g y
      

      被解析为:

      (f x) $ (g y)
      

      特别是,您有:

      sum listValid 10 = (sum listValid) 10
      sum $ listValid 10 = sum $ (listValid 10) = sum (listValid 10)
      

      【讨论】:

        猜你喜欢
        • 2016-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-25
        • 1970-01-01
        • 2011-10-10
        • 1970-01-01
        相关资源
        最近更新 更多