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