【问题标题】:Why doesn't this dollar-sign construct work?为什么这个美元符号结构不起作用?
【发布时间】:2014-02-19 08:14:00
【问题描述】:

是的,另一个美元符号问题。对不起... (我用了搜索功能!)

我的函数式编程课程教授告诉我们,美元符号 '有点添加一个左括号,然后在末尾添加一个右括号'(它以或多或少相同的方式非常粗略地描述了here)。所以

fibs = 0 : 1 : zipWith (+) fibs $ tail fibs

应该等价于

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

好吧,事实并非如此。第二件事编译正常,第一件事报错:

jkjj.hs:1:8:
    Couldn't match expected type `[a1] -> [a1]' with actual type `[a0]'
    The first argument of ($) takes one argument,
    but its type `[a0]' has none
    In the expression: 0 : 1 : zipWith (+) fibs $ tail fibs
    In an equation for `fibs':
        fibs = 0 : 1 : zipWith (+) fibs $ tail fibs

fibonacci.hs:1:16:
    Couldn't match expected type `[a0]' with actual type `[a1] -> [a1]'
    In the return type of a call of `zipWith'
    Probable cause: `zipWith' is applied to too few arguments
    In the second argument of `(:)', namely `zipWith (+) fibs'
    In the second argument of `(:)', namely `1 : zipWith (+) fibs'

当然,因为 $ 是一个函数,所以像:

fibs = 0 : 1 $ zipWith (+) fibs (tail fibs)

行不通,所以至少我的教授给出的解释过于简单化了。在写这篇文章时,我尝试放置括号,以便错误是相同的。我得到了:

fibs = (0 : 1 : zipWith (+) fibs) $ tail fibs

和

fibs = (0 : 1 : zipWith (+) fibs) (tail fibs)

它们都给了我完全相同的错误消息(当然,除了列号)。为什么是这样? a b $ c d 是否等价于 (a b) (c d) 而不是 a b (c d)?我认为这一切都与函数优先级和/或关联性有关,但我不知道具体细节。我不知道你怎么能看到一个函数的优先级(除了尝试很多组合),我也不能用谷歌找到它。

希望有人能帮我解决这个问题!

【问题讨论】:

  • 好吧,在您引用的 FP 文章中,它显示 A $ 被解释为:接下来是您正在调用的函数的最后一个参数。,所以问题是,我正在调用的函数是什么?显然,正如您所发现的那样,表达式从 $ 离开。
  • 真的!但 zipWith 似乎是“你正在调用的函数”的更明显的选择。

标签: haskell dollar-sign


【解决方案1】:

这是一个优先问题。这里有两个中缀运算符,: 和 $。作为中缀运算符,: 的优先级高于$,因此绑定更紧密。你可以在ghci中询问优先级

>> :i :
data [] a = ... | a : [a]        -- Defined in `GHC.Types`
infixr 5 :

>> :i $
($) :: (a -> b) -> a -> b
infixr 0 $

infixr 表示运算符向右分组(因此表达式 a + b + c 被解释为 a + (b + c))并且数字给出优先级(更高 = 更紧密的绑定)。

此外,您需要知道函数应用程序具有最高优先级(最紧密的绑定)。所以在你的两个表达中,这个

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

按如下方式分组

fibs = 0 : (1 : (zipWith (+) fibs (tail fibs)))

而这个

fibs = 0 : 1 : zipWith (+) fibs $ tail fibs

按如下方式分组

fibs = (0 : (1 : (zipWith (+) fibs))) $ (tail fibs)

这会给你一个错误,因为$ 左边的表达式应该是一个函数,但在你的情况下它是一个列表。

【讨论】:

    【解决方案2】:

    是的,a b $ c d 等同于 (a b) (c d)。这是因为美元符号不是一个语法结构:它和其他​​所有运算符一样是一个中缀运算符,并且必须遵守相同的规则。只是$ 应用了一个函数,并且具有非常低的优先级,从而给出了它的行为。

    您问如何找到运算符的优先级。您可以在 GHCi 中使用:info:

    ghci> :info $
    ($) :: (a -> b) -> a -> b   -- Defined in `GHC.Base'
    infixr 0 $
    

    注意最后一行。这表示它是优先级为 0 的右结合运算符。
    (优先级从 0 到 9。)

    向 GHCi 询问 : 会得到以下信息:

    ghci> :info :
    data [] a = ... | a : [a]   -- Defined in `GHC.Types'
    infixr 5 :
    

    所以:也是一个右结合运算符,但它的优先级为5。由于:与$相比具有更高的优先级,它将被分组到$的左操作数中。

    【讨论】:

    • 还要注意: 运算符的优先级为5,这似乎是他们遇到的具体问题。
    • 太棒了,正是我想要的!
    【解决方案3】:

    对:你已经发现了

    a b ! c d $ e f % g h
    

    可以理解为将括号括在表达式的两边,即

    (a b ! c d) (e f % g h)
    

    只要您只使用可以在标准 Haskell 中定义自己的东西,这就是正确的:函数和中缀运算符1。

    当您还考虑if、case 等语法时,它就不起作用了。特别是,这就是你教授的解释变得更有用的地方,lambdas 在他们的右边是“贪婪”的:

       \x -> f $ a + x
    

    表示\x -> f (a + x),而不是(\x -> f) (a + x)。但是“双方的括号”仍然有意义,只是不能包含 lambda 箭头:

       \x -> f . g $ a + x
    

    表示\x -> (f . g) (a + x),而不是\x -> f . g (a + x)。


    1原则上您可以定义一个自定义中缀运算符,其优先级与$ 一样低,但这会造成混淆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-26
      • 2022-06-20
      • 1970-01-01
      • 2021-12-07
      • 2013-09-17
      • 2013-01-11
      • 2012-08-29
      相关资源
      最近更新 更多