为什么map在Haskell中使用范围时会返回一个额外的元素?

时间:2015-09-02 23:45:59

标签: haskell range division

我刚开始学习Haskell并发现了一件奇怪的事情。

我们有一个清单:

ghci> [0,2..5]
[0,2,4]

它有3个元素。当我对此列表使用map时,我得到3个元素作为输出,例如:

ghci> map (+ 1) [0,2..5]
[1,3,5]
ghci> map (* 2) [0,2..5]
[0,4,8]
ghci> map (`div` 2) [0,2..5]
[0,1,2]

但是当我使用小数除法时,我在输出列表中得到4个元素:

ghci> map (/ 2) [0,2..5]
[0.0,1.0,2.0,3.0]
ghci> length (map (/ 2) [0,2..5])
4

您能解释为什么map可能会返回更多元素吗?

谢谢!

1 个答案:

答案 0 :(得分:11)

这是由于EnumFloat Double的实施所致:

> [0,2..5] :: [Float]
[0.0,2.0,4.0,6.0]

不是map这样做,而是Float。具体来说,如果您致电enumFromThenTo 0 2 5 :: [Float],您将获得相同的列表。您会看到Double的相同结果。

这在the haskell report暗示,但行为绝对不明显。从本质上讲,它归结为numericEnumFromThenTo(我们在这里进入一些Haskell内部)的实现,Enum Float实例使用它:

numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n')  
    where  
        p | n' >= n   = (<= m + (n' - n) / 2)  
          | otherwise = (>= m + (n' - n) / 2) 

numericEnumFromThen n m = iterate (+ (m - n)) n

所以,您numericEnumFromThen 0.0 2.0生成了列表[0.0,2.0,4.0,6.0,8.0,...],然后就此takeWhile p执行了\x -> x <= 5.0 + (2.0 - 0.0) / 2,在这种情况下,它等同于函数\x -> x <= 6.0,或更简单{ {1}},这就是6.0输出列表中包含[0.0,2.0..5.0]的原因。

我无法解释为什么以这种方式实现,这对我来说也很令人困惑,但希望我已经回答了 的实现方式。