Why does map return an additional element when using ranges in Haskell?

后端 未结 1 1641
小鲜肉
小鲜肉 2021-01-18 00:37

I\'ve just started learning Haskell and found a strange thing.

Let we have a list:

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

It has 3 elements. Whe

相关标签:
1条回答
  • 2021-01-18 00:49

    It's due to the implementation of Enum for Float and Double:

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

    It's not map doing it, but Float. Specifically, if you call enumFromThenTo 0 2 5 :: [Float], you'll get the same list. You'll see the same results for Double.

    This is hinted at in the haskell report, but the behavior is definitely non-obvious. Essentially, it comes down to the implementation of numericEnumFromThenTo (we're getting into some Haskell internals here), which is used by the Enum Float instance:

    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
    

    So you have numericEnumFromThen 0.0 2.0 generating the list [0.0,2.0,4.0,6.0,8.0,...], then you do takeWhile p on that, which in this case is equivalent to the function \x -> x <= 5.0 + (2.0 - 0.0) / 2, or more simply \x -> x <= 6.0, which is why 6.0 is included in the output list of [0.0,2.0..5.0].

    I can't explain why it's implemented this way, that's pretty baffling to me too, but hopefully I've answered the how for its implementation.

    0 讨论(0)
提交回复
热议问题