滑稽的Haskell行为:min作用于三个数字,包括一个负数

时间:2022-03-26 16:07:20

I've been playing around with some Haskell functions in GHCi.

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

I'm getting some really funny behaviour and I'm wondering why it's happening.

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

I realized that the function min is only supposed to be used with two values. However, when I use three values, in my case

我意识到函数最小值只应该用于两个值。但是,在我的例子中,当我使用三个值时

min 1 2 -5

I'm getting

我得到

-4

as my result.

我的结果。

Why is that?

这是为什么呢?

3 个解决方案

#1


28  

You are getting that result because this expression:

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

min 1 2 -5

parses as if it were parenthesized like this:

像这样的括号:

(min 1 2) -5

which is the same as this:

这是一样的:

1 -5

which is the same as this:

这是一样的:

1 - 5

which is of course -4.

当然是-4。

In Haskell, function application is the most tightly-binding operation, but it is not greedy. In fact, even a seemingly simple expression like min 1 2 actually results in two separate function calls: the function min is first called with a single value, 1; the return value of that function is a new, anonymous function, which will return the smaller value between 1 and its single argument. That anonymous function is then called with an argument of 2, and of course returns 1. So a more accurate fully-parenthesized version of your code is this:

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

((min 1) 2) - 5

To find the minimum of three values, you need to chain together two calls to min:

要找到三个值的最小值,需要将两个调用链接到min:

min (min 1 2) (-5)

Technically, the above code actually results in four separate function calls, per the logic outlined earlier - making that explicit would look something like the code below, but it's fine to think of it as just two calls to min:

从技术上讲,上面的代码实际上产生了四个独立的函数调用,按照前面概述的逻辑——使显式看起来类似于下面的代码,但是可以把它看作是对min的两个调用:

(min ((min 1) 2)) (-5)

The parentheses around -5 ensure that the - is interpreted as prefix negation instead of infix subtraction. In general, if you have a literal negative number in Haskell, the parentheses are necessary. In some cases you can leave them off, but using them makes things clearer for the reader of your code.

在-5附近的圆括号确保-被解释为前缀否定而不是中间减法。通常,如果Haskell中有一个文字负数,括号是必要的。在某些情况下,您可以不使用它们,但是使用它们可以使代码的读者更清楚。

More generally, you could let Haskell do the chaining for you by applying a fold to a list, which can then contain as many numbers as you like:

更一般地说,你可以让Haskell为你做链接,将一个折叠应用到一个列表中,这个列表可以包含任意数量的数字:

foldl1 min [1, 2, -5]

(Note that an item in a list is one of the contexts where you can leave the parentheses off the literal negative number.)

(请注意,列表中的项目是可以将圆括号从文字负数中删除的上下文之一。)

The call foldl1 fun list means "take the first two items of list and call fun on them. Then take the result of that call and the next item of list, and call fun on those two values. Then take the result of that call and the next item of the list..." And so on, continuing until there's no more list, at which point the value of the last call to fun is returned to the original caller.

The call foldl1 fun list意思是“取列表的前两项,然后调用fun。”然后获取该调用的结果和列表的下一项,并对这两个值调用fun。然后取那个电话的结果和列表的下一项……继续,直到没有列表,这时最后一次调用fun的值返回给原始调用者。

There are several functions that have predefined pre-folded equivalents, however, and min is one of them; the list version is called minimum:

不过,有几个函数具有预定义的预折叠对等项,最小值就是其中之一;该列表版本被称为最小值:

minimum [1, 2, -5]

That behaves exactly like my foldl1 solution above; in particular, both will throw an error if handed an empty list.

就像上面的foldl1溶液一样;特别地,如果传递一个空列表,两者都会抛出一个错误。

Thanks to JohnL for reminding me of the existence of minimum.

感谢约翰提醒我最小值的存在。

#2


7  

When you type min 1 2 -5, Haskell doesn't group it as min 1 2 (-5), as you seem to think. It instead interprets it as (min 1 2) - 5, that is, it does subtraction rather than negation. The minimum of 1 and 2 is 1, obviously, and subtracting 5 from that will (perfectly correctly) give you -4.

当您键入min 1 2 -5时,Haskell不会将其分组为最小值1 2(-5),就像您认为的那样。它把它解释为(min 1 2) - 5,也就是说,它做减法而不是否定。显然,1和2的最小值是1,从中减去5就会得到-4。

Generally, in Haskell, you should surround negative numbers with parentheses so that this kind of stuff doesn't happen unexpectedly.

一般来说,在Haskell中,应该用圆括号括住负数,这样就不会出现这种情况。

#3


0  

Nothing to add to the previous answers. But you are probably looking for this function.

没有什么可补充的。但是你可能在寻找这个函数。

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

#1


28  

You are getting that result because this expression:

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

min 1 2 -5

parses as if it were parenthesized like this:

像这样的括号:

(min 1 2) -5

which is the same as this:

这是一样的:

1 -5

which is the same as this:

这是一样的:

1 - 5

which is of course -4.

当然是-4。

In Haskell, function application is the most tightly-binding operation, but it is not greedy. In fact, even a seemingly simple expression like min 1 2 actually results in two separate function calls: the function min is first called with a single value, 1; the return value of that function is a new, anonymous function, which will return the smaller value between 1 and its single argument. That anonymous function is then called with an argument of 2, and of course returns 1. So a more accurate fully-parenthesized version of your code is this:

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

((min 1) 2) - 5

To find the minimum of three values, you need to chain together two calls to min:

要找到三个值的最小值,需要将两个调用链接到min:

min (min 1 2) (-5)

Technically, the above code actually results in four separate function calls, per the logic outlined earlier - making that explicit would look something like the code below, but it's fine to think of it as just two calls to min:

从技术上讲,上面的代码实际上产生了四个独立的函数调用,按照前面概述的逻辑——使显式看起来类似于下面的代码,但是可以把它看作是对min的两个调用:

(min ((min 1) 2)) (-5)

The parentheses around -5 ensure that the - is interpreted as prefix negation instead of infix subtraction. In general, if you have a literal negative number in Haskell, the parentheses are necessary. In some cases you can leave them off, but using them makes things clearer for the reader of your code.

在-5附近的圆括号确保-被解释为前缀否定而不是中间减法。通常,如果Haskell中有一个文字负数,括号是必要的。在某些情况下,您可以不使用它们,但是使用它们可以使代码的读者更清楚。

More generally, you could let Haskell do the chaining for you by applying a fold to a list, which can then contain as many numbers as you like:

更一般地说,你可以让Haskell为你做链接,将一个折叠应用到一个列表中,这个列表可以包含任意数量的数字:

foldl1 min [1, 2, -5]

(Note that an item in a list is one of the contexts where you can leave the parentheses off the literal negative number.)

(请注意,列表中的项目是可以将圆括号从文字负数中删除的上下文之一。)

The call foldl1 fun list means "take the first two items of list and call fun on them. Then take the result of that call and the next item of list, and call fun on those two values. Then take the result of that call and the next item of the list..." And so on, continuing until there's no more list, at which point the value of the last call to fun is returned to the original caller.

The call foldl1 fun list意思是“取列表的前两项,然后调用fun。”然后获取该调用的结果和列表的下一项,并对这两个值调用fun。然后取那个电话的结果和列表的下一项……继续,直到没有列表,这时最后一次调用fun的值返回给原始调用者。

There are several functions that have predefined pre-folded equivalents, however, and min is one of them; the list version is called minimum:

不过,有几个函数具有预定义的预折叠对等项,最小值就是其中之一;该列表版本被称为最小值:

minimum [1, 2, -5]

That behaves exactly like my foldl1 solution above; in particular, both will throw an error if handed an empty list.

就像上面的foldl1溶液一样;特别地,如果传递一个空列表,两者都会抛出一个错误。

Thanks to JohnL for reminding me of the existence of minimum.

感谢约翰提醒我最小值的存在。

#2


7  

When you type min 1 2 -5, Haskell doesn't group it as min 1 2 (-5), as you seem to think. It instead interprets it as (min 1 2) - 5, that is, it does subtraction rather than negation. The minimum of 1 and 2 is 1, obviously, and subtracting 5 from that will (perfectly correctly) give you -4.

当您键入min 1 2 -5时,Haskell不会将其分组为最小值1 2(-5),就像您认为的那样。它把它解释为(min 1 2) - 5,也就是说,它做减法而不是否定。显然,1和2的最小值是1,从中减去5就会得到-4。

Generally, in Haskell, you should surround negative numbers with parentheses so that this kind of stuff doesn't happen unexpectedly.

一般来说,在Haskell中,应该用圆括号括住负数,这样就不会出现这种情况。

#3


0  

Nothing to add to the previous answers. But you are probably looking for this function.

没有什么可补充的。但是你可能在寻找这个函数。

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