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

Vad*_*dik 8 haskell range division

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

我们有一个清单:

ghci> [0,2..5]
[0,2,4]
Run Code Online (Sandbox Code Playgroud)

它有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]
Run Code Online (Sandbox Code Playgroud)

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

ghci> map (/ 2) [0,2..5]
[0.0,1.0,2.0,3.0]
ghci> length (map (/ 2) [0,2..5])
4
Run Code Online (Sandbox Code Playgroud)

你能否解释为什么map可能会返回更多的元素呢?

谢谢!

bhe*_*ilr 11

这是由于Enumfor Float和的实现Double:

> [0,2..5] :: [Float]
[0.0,2.0,4.0,6.0]
Run Code Online (Sandbox Code Playgroud)

它没有map这样做,但是Float.具体来说,如果你打电话enumFromThenTo 0 2 5 :: [Float],你会得到相同的清单.你会看到相同的结果Double.

这在haskell报告中暗示,但行为绝对不明显.从本质上讲,它归结为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
Run Code Online (Sandbox Code Playgroud)

所以你已经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,这就是为什么6.0包含在输出列表中的原因[0.0,2.0..5.0].

我无法解释为什么它的这种方式来实现,这是相当令人费解对我来说太,但希望我已经回答了如何为它的实现.

  • 为什么重要的是`[0,0.1..1]`总是有11个元素,而真正重要的是`[0,2..5]`有多少元素,因为它首先是无意义的. (5认同)
  • 为了详细说明@ReidBarton,浮点范围的实现被设计成当起点和终点之间的差值是*大约*整个步数时,长度对常见情况下的舍入误差不敏感.相反,截止位置处于不太可能的"相反"情况下,即超过半步. (2认同)