46

我正在尝试为自己验证有关 Haskell 中运算符和函数优先级的一些信息。例如下面的代码

list = map foo $ xs

可以改写为

list = (map foo) $ (xs)

并且最终会

list = map foo xs

我的问题曾经是,为什么第一个公式不会被重写为

list = (map foo $) xs

因为函数优先级总是高于运算符优先级,但我认为我已经找到了答案:运算符根本不允许作为函数的参数(当然,除非你用括号括起来)。这是正确的吗?如果是这样,我觉得很奇怪,在 RWH 或 Learn you a Haskell 或我搜索过的任何其他地方都没有提到这个机制/规则。因此,如果您知道一个地方,其中规定了规则,请链接到它。

-- 编辑:感谢您的快速回答。我认为我的困惑来自于认为运算符文字会以某种方式评估为某些东西,这可能会被函数用作参数。它帮助我记住,中缀运算符可以机械地转换为前缀函数。对第一个配方这样做会产生

($) (map foo) (xs)

其中 ($) 是消费函数是毫无疑问的,而且由于两个公式是等价的,那么第一个公式中的 $ 字面量就不能被 map 消费。

4

5 回答 5

39

首先,应用程序(空白)是最高优先级的“运算符”。

其次,在 Haskell 中,运算符和函数之间实际上没有区别,除了运算符默认是中缀的,而函数不是。您可以使用反引号将函数转换为中缀

2 `f` x

并将运算符转换为带括号的前缀:

(+) 2 3

所以,你的问题有点混乱。

现在,特定的函数和运算符将声明优先级,您可以在 GHCi 中使用 ":info" 找到它们:

Prelude> :info ($)
($) :: (a -> b) -> a -> b   -- Defined in GHC.Base

infixr 0 $

Prelude> :info (+)

class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a

infixl 6 +

显示优先级和关联性。

于 2010-06-26T20:30:52.703 回答
28

你是对的。该规则是Haskell Report定义的 Haskell 语法的一部分。在第 3 节,表达式中特别注意,函数应用程序 (an fexp) 的参数必须是 an aexp。aexp 允许运算符作为部分的一部分,也允许在括号表达式中,但不允许裸运算符。

map foo $ xs中,Haskell 语法意味着这被解析为两个表达式,应用于二元运算符$。正如 sepp2k 所指出的,语法(map foo $)是左侧部分并且具有不同的含义。

我不得不承认我从来没有考虑过这个问题,实际上不得不在报告中查找它,以了解为什么运营商会有他们的行为。

于 2010-06-26T21:56:38.027 回答
25

除了其他答案已经提供的信息之外,请注意,不同的运算符可以比其他运算符具有不同的优先级,以及左/右或非关联。您可以在Haskell 98 Report fixity 部分Prelude找到运算符的这些属性。

+--------+----------------------------------+----- ------+--------------------------------+
| Prec- | 左联想 | 非关联 | 右结合 |
| 结语 | 运营商 | 运营商 | 运营商 |
+--------+----------------------------------+----- ------+--------------------------------+
| 9 | !!| | . |
| 8 | | | ^, ^^, ** |
| 7 | *, /, `div`, | | |
| | `mod`、`rem`、`quot` | | |
| 6 | +, - | | |
| 5 | | | :, ++ |
| 4 | | ==, /=, <, <=, >, >=, | |
| | | `elem`, `notElem` | |
| 3 | | | && |
| 2 | | | || |
| 1 | >>, >>= | | |
| 0 | | | $, $!, `seq` |
+--------+----------------------------------+----- ------+--------------------------------+

任何缺少固定性声明的运算符都被假定为与优先级 9 关联

请记住,函数应用程序具有最高优先级(考虑10与表中其他优先级相比的优先级)[1]

于 2015-05-17T16:05:31.290 回答
12

不同之处在于中缀运算符放置在它们的参数之间,所以这

list = map foo $ xs

可以用前缀形式重写为

list = ($) (map foo) xs

根据 $ 运算符的定义,它就是

list = (map foo) xs
于 2010-06-26T20:33:39.040 回答
10

运算符可以作为函数参数传递,如果你用括号括起来(即map foo ($) xs,它确实会作为 传递(map foo ($)) xs)。但是,如果您不使用括号将它们括起来,那么您是正确的,它们不能作为参数传递(或分配给变量)。

另请注意,语法(someValue $)(其中$可以是任何运算符)实际上意味着不同的东西:它等同于\x -> someValue $ x,即它部分地将运算符应用于其左操作数($当然,在这种情况下是 noop)。同样($ x),将运算符部分应用于右操作数。所以map ($ x) [f, g, h]会评估为[f x, g x, h x].

于 2010-06-26T20:29:42.987 回答