12

我一直在 GHCi 中使用一些 Haskell 函数。

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

我意识到该函数min只应该与两个值一起使用。但是,当我使用三个值时,就我而言

min 1 2 -5

我越来越

-4

作为我的结果。

这是为什么?

4

3 回答 3

32

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

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,使其显然不是减法运算,因此此处不需要括号。)

调用fun列表的意思是“获取list的前两项并对它们调用fun。然后获取该调用的结果和 list 的下一项并对这两个值调用fun。然后获取该调用的结果和列表的下一项...” 以此类推,直到没有更多的list,此时最后一次调用fun的值返回给原始调用者。foldl1

对于 的特定情况min,已经为您定义了一个折叠版本:minimum。所以你也可以这样写:

minimum [1, 2, -5]

这与我的解决方案完全一样foldl1;特别是,如果传递一个空列表,两者都会抛出错误,而如果传递一个单元素列表,它们将返回该元素不变而无需调用min.

感谢 JohnL 提醒我minimum.

于 2013-02-07T00:34:01.410 回答
7

当您键入 时min 1 2 -5,Haskell 并没有min 1 2 (-5)像您想象的那样将其分组为 。而是将其解释为(min 1 2) - 5,也就是说,它进行减法而不是求反。1和的最小值显然215从中减去将(完全正确)给你-4

通常,在 Haskell 中,您应该用括号将负数括起来,这样这种事情就不会意外发生。

于 2013-02-07T00:37:22.033 回答
0

没有什么可以添加到以前的答案中。但您可能正在寻找此功能。

import Data.List
minimum [1, 2, -4]
于 2017-04-25T05:21:20.127 回答