3

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

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

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)

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

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

4

3 回答 3

9

这是一个优先问题。您在这里有两个中缀运算符,:并且$. 作为中缀运算符,:具有比 更高的优先级$,因此它的绑定更紧密。您可以询问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)

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

于 2014-01-28T08:45:36.690 回答
7

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

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

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 :

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

于 2014-01-28T06:51:33.373 回答
3

对:你已经发现了

a b ! c d $ e f % g h

可以理解为在两边的表达式周围放置括号

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

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

if当您还考虑, caseetc. 语法时,它不起作用的地方。特别是,这就是你教授的解释变得更加有用的地方,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原则上,您可以定义一个具有与 一样低优先级的自定义中缀运算符$,但这会造成混淆。

于 2014-01-28T13:11:01.870 回答