我一直在玩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
.
当你输入时min 1 2 -5
,Haskell并不min 1 2 (-5)
像你想象的那样将它分组.它反而把它解释为(min 1 2) - 5
,即它做减法而不是否定.最小1
和2
是1
,很明显,减去5
从将(完全正确)给你-4
.
一般来说,在Haskell中,你应该用括号括起负数,这样这种东西就不会意外发生.