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

Cod*_*ein 11 haskell

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

我有一些非常有趣的行为,我想知道它为什么会发生.

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

min 1 2 -5
Run Code Online (Sandbox Code Playgroud)

我越来越

-4
Run Code Online (Sandbox Code Playgroud)

作为我的结果.

这是为什么?

Mar*_*eed 31

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

min 1 2 -5
Run Code Online (Sandbox Code Playgroud)

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

(min 1 2) -5
Run Code Online (Sandbox Code Playgroud)

这与此相同:

1 -5
Run Code Online (Sandbox Code Playgroud)

这与此相同:

1 - 5
Run Code Online (Sandbox Code Playgroud)

这当然是-4.

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

((min 1) 2) - 5
Run Code Online (Sandbox Code Playgroud)

要找到最少三个值,您需要将两个调用链接在一起min:

min (min 1 2) (-5)
Run Code Online (Sandbox Code Playgroud)

从技术上讲,上面的代码实际上导致了四个单独的函数调用,按照前面描述的逻辑 - 使显式看起来像下面的代码,但是可以把它看作只是两次调用-5:

foldl1 min [1, 2, -5]
Run Code Online (Sandbox Code Playgroud)

括号周围-确保将其-5解释为前缀否定而不是中缀减法.通常,如果在Haskell中有一个文字负数,则括号是必需的.在某些情况下,您可以将它们关闭,但使用它们可以使代码的读者更清楚.

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

minimum [1, 2, -5]
Run Code Online (Sandbox Code Playgroud)

(请注意,列表中的项是其中一个上下文,您可以将括号从字面负数中删除.)

呼叫乐趣名单意味着"走的前两个项目名单,并呼吁乐趣在他们身上.然后将您的呼叫和的下一个项目的结果列表,并呼吁乐趣上这两个值,然后将您的呼叫和结果列表的下一项......"依此类推,继续直到没有更多列表,此时最后一次有趣的呼叫的值将返回给原始呼叫者.foldl1

然而,有几个函数具有预定义的预折叠等价物,并且min是其中之一; 列表版本被调用minimum:

min 1 2 -5
Run Code Online (Sandbox Code Playgroud)

这与我foldl1上面的解决方案完全一样; 特别是,如果交给空列表,两者都会抛出错误.

感谢JohnL提醒我存在的min.

  • 还有`minimum`函数,它直接在列表上运行.这是一个部分功能,所以只有当你可以保证列表至少有一个元素时才应该使用它. (6认同)
  • 我在哪里可以找到有关折叠的更多信息? (2认同)
  • 我补充说明.有关更多信息,请访问:http://en.wikipedia.org/wiki/Fold_(higher-order_function).或者直接跳到Haskell细节:http://www.haskell.org/haskellwiki/Fold (2认同)

Pth*_*ame 7

当你输入时min 1 2 -5,Haskell并不min 1 2 (-5)像你想象的那样将它分组.它反而把它解释为(min 1 2) - 5,即它做减法而不是否定.最小121,很明显,减去5从将(完全正确)给你-4.

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