Haskell函数中的括号问题

3

我只想知道我们如何知道哪些函数需要使用括号(),哪些不需要?例如:

replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))

工作正常。但是

replicate 100 (product (map (*3) (zipWith (max [1,2,3,4,5] [4,5,6,7,8]))))

代码不能正常运行。原因是我在使用zipWith时加了一组括号。在这个小例子中,zipWith和max没有括号,而replicate、product和map有。通常有没有办法知道/弄清楚哪些函数需要括号,哪些不需要呢?


在前者中,zipWith 应用于 3 个参数;函数 max 和 2 个列表。而在后者中,它仅应用于一个参数;即将 max 应用于 2 个列表的结果。 - pat
在这种情况下,您可以通过谨慎使用函数组合来消除大部分括号: replicate 100 . product . map (*3) $ zipWith max [1,2,3,4,5] [4,5,6,7,8] - pat
@pat 是的,但对于正在弄清括号功能的人来说并不是很有帮助。 - Cubic
2个回答

15

函数应用是左结合的。因此,当您编写类似以下的表达式时:

f g h x

它的意思是:

((f g) h) x

而且zipWith的类型也提供了一个线索:

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

它说zipWith有3个参数:一个函数和两个列表。

当你写:

zipWith (max [1,2,3,4,5] [4,5,6,7,8])

解释器将会理解这一点

max [1,2,3,4,5] [4,5,6,7,8]

将是传递给zipWith的第一个参数,但类型不正确。请注意,zipWith期望一个带有两个参数的函数作为其第一个参数,并且如@Cubic所指出的,max [1,2,3,4,5] [4,5,6,7,8]将返回这两个列表之间的最大值,根据通常的字典序排序,这将是类型为[a]的值,其中aOrdNum的实例。因此,错误变得明显,因为你试图传递一个类型为

(Num a, Ord a) => [a]

其中一个类型的值为

(a -> b -> c)

预计会发生。


6
值得指出的是,虽然你不能将max [1,2,3,4,5] [4,5,6,7,8]作为zipWith函数的第一个参数使用,但它本身是一个正确的表达式(会给出两个列表中字典序较大的那个)。 - Cubic
@Srinivas:不客气。如果我的回答可以,请将其标记为您问题的答案。 - Rodrigo Ribeiro

5

Rodrigo给出了正确的答案。我只想补充一点,有些人错误地认为某些函数需要括号,而另一些则不需要。

这就像在学校里学数学一样:

3 * (4+5)

通常情况下,+表达式需要括号,而*表达式则不需要。

在Haskell中,你总是可以完全不用括号。每当你需要将一个表达式括起来时,另一种选择是引入一个局部名称并将其绑定到该表达式,然后使用该名称代替表达式。

以你的例子为例:

replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))

let list1 = product list2
    list2 = map thrice list3
    thrice x = x*3
    list3 = zipWith max [1,2,3,4,5] [4,5,6,7,8]
in replicate 100 list1

事实上,我经常从上到下编写函数,像这样:
foo x y z = result
  where
   result = ...
   ...

然而,正如之前所说,由函数应用组成的表达式也经常可以通过使用 (.)($) 来省略括号来写出,在这种情况下,从上面的自上而下的方法可能过于冗长,下面的方法会更加清晰(因为没有通过新引入的名称产生噪音):

replicate 100
  . product
  . map (*3)
  $ zipWith max [1..5] [4..8]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接