有趣的Haskell行为:三个数字的最小函数,包括负数

时间:2013-02-07 00:30:56

标签: haskell

我一直在玩GHCi中的一些Haskell函数。

我得到了一些非常有趣的行为,我想知道为什么会这样。

我意识到函数min只能与两个值一起使用。但是,当我使用三个值时,在我的情况下

min 1 2 -5

我正在

-4

作为我的结果。

为什么?

3 个答案:

答案 0 :(得分:31)

你得到的结果是因为这个表达式:

min 1 2 -5

解析好像它是这样的括号:

(min 1 2) -5

与此相同:

1 -5

与此相同:

1 - 5

当然是-4。

在Haskell中,函数应用程序是绑定最紧密的操作,但并不贪心。实际上,即使像min 1 2这样看似简单的表达式实际上也会导致两个单独的函数调用:函数min首先使用单个值1调用;该函数的返回值是一个新的匿名函数,它将返回1和它的单个参数之间的较小值。然后使用参数2调用该匿名函数,当然返回1.因此,代码的更准确的完全括号版本是:

((min 1) 2) - 5

但是我不会打破那么远的一切;对于大多数目的而言,看起来像具有多个参数的函数调用实际上变成一系列多个单参数函数调用的事实是一个手动的实现细节。重要的是要知道,如果你向一个函数传递的参数太少,你就会得到一个可以用其余参数调用的函数,但是大多数时候你可以忽略这样的事实,即这些调用是在封面下发生的事情。即使你传递正确数量的参数也是为了开始。

因此,为了找到三个值的最小值,您需要将两个调用链接到min(实际上每个逻辑上面有四个调用,但是再一次,用手挥动):

min (min 1 2) (-5)

-5周围的括号必须确保-被解释为前缀否定而不是中缀减法;如果没有它们,你会遇到与原始代码相同的问题,只有这次你会要求Haskell从函数中减去一个数字并得到一个类型错误。

更一般地说,您可以让Haskell通过将折叠应用于列表来为您进行链接,然后列表可以包含任意数量的数字:

foldl1 min [1, 2, -5]

(请注意,在文字列表语法中,逗号和方括号分隔-5,使其显然不是减法操作,因此您不需要括号。)

致电foldl1 有趣 列表表示“取列表的前两项并致电有趣 on the。然后获取该调用的结果和 list 的下一项,并在这两个值上调用 fun 。然后获取该调用的结果和列表的下一项...“依此类推,继续直到没有列表,此时最后一次调用 fun 的值将返回到原始值呼叫者。

对于min的特定情况,已经为您定义了折叠版本:minimum。所以你也可以用这种方式写上面的内容:

minimum [1, 2, -5]

其行为与我的foldl1解决方案完全相同;特别是,如果交给一个空列表,两者都会抛出一个错误,而如果递给一个单元素列表,它们将返回该元素而不调用min

感谢JohnL提醒我minimum

的存在

答案 1 :(得分:7)

当您键入min 1 2 -5时,Haskell不会将其分组为min 1 2 (-5),正如您所想的那样。它反而把它解释为(min 1 2) - 5,也就是说,它做减法而不是否定。显然,12的最小值为1,从中减去5(完全正确)会给您-4

一般来说,在Haskell中,你应该用括号括起负数,这样这种东西就不会意外发生。

答案 2 :(得分:0)

没有什么可以添加到以前的答案。但你可能正在寻找这个功能。

import Data.List
minimum [1, 2, -4]