Haskell 不确定如何使用`$`函数

Haskell 不确定如何使用`$`函数,haskell,operators,brackets,Haskell,Operators,Brackets,当我试图编写一个函数来转换列表列表时,我看到了一些非常奇怪的东西。我试过: > let abc xs | null (head xs) = [] | otherwise = map head xs : abc $ map tail xs 我犯了一个错误。然后我试着: > let abc xs | null (head xs) = [] | otherwise = map head xs : abc ( map tail xs ) > abc [[1,2,3], [4,5,6]

当我试图编写一个函数来转换列表列表时,我看到了一些非常奇怪的东西。我试过:

> let abc xs | null (head xs) = [] | otherwise = map head xs : abc $ map tail xs
我犯了一个错误。然后我试着:

> let abc xs | null (head xs) = [] | otherwise = map head xs : abc ( map tail xs )
> abc [[1,2,3], [4,5,6], [7,8,9]]
[[1,4,7],[2,5,8],[3,6,9]]

我开始相信可以使用
$
操作符来代替括号,这更像是哈斯凯尔式的。为什么会出现错误?

运算符是可以应用于中缀位置的函数。因此,
$
是一个函数

在Haskell中,您可以定义自己的函数,这些函数可以在参数之间的中缀位置使用。然后,您还可以使用
infix
infixr
infixl
定义函数应用程序优先级和关联性-也就是说,提示编译器是否将
a$b$c
视为
(a$b)$c
,或
a$(b$c)

$
的优先级使得第一个表达式的解释类似于
(映射头xs:abc)$…

例如,要将
$
声明为中缀,请将其名称放入
()

或组成:

(.) :: (b->c)->(a->b)->a->c
(f . g) x = f $ g x
算术“运算符”也被定义为Num类中的中缀函数

此外,您还可以在应用程序站点的backticks``中引用其他函数作为中缀。有时,它会使表达看起来更漂亮:

f `map` xs == map f xs

(并不是说在这种特殊情况下它看起来很漂亮,只是为了展示一个简单的例子)

操作符是一个可以应用于中缀位置的函数。因此,
$
是一个函数

在Haskell中,您可以定义自己的函数,这些函数可以在参数之间的中缀位置使用。然后,您还可以使用
infix
infixr
infixl
定义函数应用程序优先级和关联性-也就是说,提示编译器是否将
a$b$c
视为
(a$b)$c
,或
a$(b$c)

$
的优先级使得第一个表达式的解释类似于
(映射头xs:abc)$…

例如,要将
$
声明为中缀,请将其名称放入
()

或组成:

(.) :: (b->c)->(a->b)->a->c
(f . g) x = f $ g x
算术“运算符”也被定义为Num类中的中缀函数

此外,您还可以在应用程序站点的backticks``中引用其他函数作为中缀。有时,它会使表达看起来更漂亮:

f `map` xs == map f xs

(并不是说在这种特殊情况下它看起来很漂亮,只是为了展示一个简单的示例)

除了Sassa的正确答案之外,让我们进一步剖析您提供的代码片段:

map head xs : abc $ map tail xs
这里使用两个运算符:
(:)
($)
。如上所述,默认情况下,它们被解释为中缀,因为它们的名称仅由符号组成

每个操作符都有一个优先级,它决定了它绑定的“紧密程度”,或者更有用的是,先应用哪个操作符。您的代码可以解释为

((map head xs) : abc) $ (map tail xs)
其中,
(:)
绑定更紧密(以前应用过)
($)

(map head xs) : (abc $ (map tail xs))
其中,
($)
绑定更紧密。请注意,我在函数应用程序周围加了括号(例如
map
应用于
tail
xs
)。函数应用程序比任何操作符绑定得更紧密,因此总是首先应用

为了确定这两种解释代码的方法中哪一种是正确的,编译器需要获得关于哪个操作符应该绑定得更紧密的信息。这是使用固定性声明完成的,如

infix 8
或者总体上

infix i
其中
i
介于0和9之间。
i
的值越高,表示操作符绑定得越紧。(
infixr
infixl
可用于额外定义关联性,如Sassa的回答中所述,但这不会影响您的具体问题。)

事实证明,
($)
操作符的固定性声明是

infixr 0 $
如图所示<代码>(:)更“神奇”,因为它是硬编码到Haskell语法中的,但Haskell报告它具有优先级5

现在,我们终于有了足够的信息,可以断定您的代码的第一个解释确实是正确的:
(:)
,5的优先级高于
($)
,0的优先级。作为一般的经验法则,
($)
通常不能与其他操作员很好地交互


顺便说一句,如果一个表达式包含两个具有相同优先级的不同运算符(例如),则它们的应用顺序不清楚,因此必须使用括号明确指定它。

除了Sassa的正确答案之外,让我们进一步剖析您提供的代码片段:

map head xs : abc $ map tail xs
这里使用两个运算符:
(:)
($)
。如上所述,默认情况下,它们被解释为中缀,因为它们的名称仅由符号组成

每个操作符都有一个优先级,它决定了它绑定的“紧密程度”,或者更有用的是,先应用哪个操作符。您的代码可以解释为

((map head xs) : abc) $ (map tail xs)
其中,
(:)
绑定更紧密(以前应用过)
($)

(map head xs) : (abc $ (map tail xs))
其中,
($)
绑定更紧密。请注意,我在函数应用程序周围加了括号(例如
map
应用于
tail
xs
)。函数应用程序比任何操作符绑定得更紧密,因此总是首先应用

为了确定这两种解释代码的方法中哪一种是正确的,编译器需要获得关于哪个操作符应该绑定得更紧密的信息。这是使用固定性声明完成的,如

infix 8
或者总体上

infix i
其中
i
介于0和9之间。
i
的值越高,表示操作符绑定得越紧。(
infixr
infixl
可用于额外定义关联性,如SASA的回答中所述,但这不会影响您的规范