如果我在 GHCI 上输入 [1/0..1/0],我将得到 Infinite Infinity。为什么?
If I enter [1/0..1/0] on GHCI I am getting Infinite Infinity. Why?
我无法理解 Haskell 中范围的以下行为。枚举 1 到 1 给我一个仅包含 1 的列表,而 2 到 2 给我一个仅包含 2 的列表,如下所示。
Prelude> [1..1]
[1]
Prelude> [2..2]
[2]
但是从无穷大到无穷大的枚举得到了一个长度无穷大且所有元素都是无穷大的列表,如下所示。
Prelude> [1/0..1/0]
[Infinity,Infinity,Infinity,Infinity,Infinity,Infinity,Interrupted.
我知道无穷大是一个不能被视为数字的概念,但是什么可以证明这种行为是合理的?
Haskell 的 Double
(使用 /
时得到的默认值)遵循浮点数的 IEEE 754 standard,它定义了 Infinity
表现。这就是 1/0
是 Infinity
的原因。
根据这个标准(并且,公平地说,根据逻辑),Infinity + 1 == Infinity
,Double
的 Enum
实例每次只添加 1
。
这是另一个迹象,表明 Double
的 Enum
实例的格式不完全正确,并且不符合我们通常对 Enum
实例的期望。因此,根据经验,您应该避免将 ..
用于浮点数:即使 您 理解它是如何工作的,其他人阅读您的代码也会感到困惑.作为混淆行为的另一个例子,请考虑:
Prelude> [0.1..1]
[0.1,1.1]
如果您想深入了解有关Double
的Enum
实例的更多详细信息,您可以阅读此pretty lengthy Haskell-cafe thread .
我相信 [a..b]
的实现会继续递增 a
直到它大于 b
。无穷大永远不会发生这种情况,因此它会永远存在。
我相信您的代码可能会默认 Double
您编写它的方式,它具有明确定义的无限语义。 IIRC,Haskell 跟随 http://en.wikipedia.org/wiki/IEEE_floating_point。
正如 Luis Wasserman 和 Tikhon Jelvis 指出的那样,基本问题是 Float
和 Double
的 Num
和 Enum
实例是 很奇怪,而且真的根本不应该存在。事实上,Enum
class 本身很奇怪,因为它试图同时服务于几个不同的目的,其中 none 很好——最好将其视为历史事故和便利而不是 typeclass 应该是什么样子的一个很好的例子。在很大程度上,Num
和 Integral
class 也是如此。使用这些 classes 中的任何一个时,您必须密切注意您正在操作的特定类型。
浮点数的enumFromTo
方法基于以下函数:
numericEnumFromTo :: (Ord a, Fractional a) => a -> a -> [a]
numericEnumFromTo n m = takeWhile (<= m + 1/2) (numericEnumFrom n)
所以2 <= 1+1/2
是false,但是由于浮点数的怪异,infinity <= infinity + 1/2
是true .
正如 Tikhon Jelvis 指出的那样,通常最好不要使用任何 Enum
方法,包括数值范围和浮点数。
我无法理解 Haskell 中范围的以下行为。枚举 1 到 1 给我一个仅包含 1 的列表,而 2 到 2 给我一个仅包含 2 的列表,如下所示。
Prelude> [1..1]
[1]
Prelude> [2..2]
[2]
但是从无穷大到无穷大的枚举得到了一个长度无穷大且所有元素都是无穷大的列表,如下所示。
Prelude> [1/0..1/0]
[Infinity,Infinity,Infinity,Infinity,Infinity,Infinity,Interrupted.
我知道无穷大是一个不能被视为数字的概念,但是什么可以证明这种行为是合理的?
Haskell 的 Double
(使用 /
时得到的默认值)遵循浮点数的 IEEE 754 standard,它定义了 Infinity
表现。这就是 1/0
是 Infinity
的原因。
根据这个标准(并且,公平地说,根据逻辑),Infinity + 1 == Infinity
,Double
的 Enum
实例每次只添加 1
。
这是另一个迹象,表明 Double
的 Enum
实例的格式不完全正确,并且不符合我们通常对 Enum
实例的期望。因此,根据经验,您应该避免将 ..
用于浮点数:即使 您 理解它是如何工作的,其他人阅读您的代码也会感到困惑.作为混淆行为的另一个例子,请考虑:
Prelude> [0.1..1]
[0.1,1.1]
如果您想深入了解有关Double
的Enum
实例的更多详细信息,您可以阅读此pretty lengthy Haskell-cafe thread .
我相信 [a..b]
的实现会继续递增 a
直到它大于 b
。无穷大永远不会发生这种情况,因此它会永远存在。
我相信您的代码可能会默认 Double
您编写它的方式,它具有明确定义的无限语义。 IIRC,Haskell 跟随 http://en.wikipedia.org/wiki/IEEE_floating_point。
正如 Luis Wasserman 和 Tikhon Jelvis 指出的那样,基本问题是 Float
和 Double
的 Num
和 Enum
实例是 很奇怪,而且真的根本不应该存在。事实上,Enum
class 本身很奇怪,因为它试图同时服务于几个不同的目的,其中 none 很好——最好将其视为历史事故和便利而不是 typeclass 应该是什么样子的一个很好的例子。在很大程度上,Num
和 Integral
class 也是如此。使用这些 classes 中的任何一个时,您必须密切注意您正在操作的特定类型。
浮点数的enumFromTo
方法基于以下函数:
numericEnumFromTo :: (Ord a, Fractional a) => a -> a -> [a]
numericEnumFromTo n m = takeWhile (<= m + 1/2) (numericEnumFrom n)
所以2 <= 1+1/2
是false,但是由于浮点数的怪异,infinity <= infinity + 1/2
是true .
正如 Tikhon Jelvis 指出的那样,通常最好不要使用任何 Enum
方法,包括数值范围和浮点数。