Fractionalな数列の謎仕様
mapするとリストの長さが変わるという、大変愉快な事態に遭遇しました。
Prelude> length $ [0,10..35] 4 Prelude> length $ map (/ 5) [0,10..35] 5
なんじゃこりゃーと思っていろいろ試してみる。
Prelude> [0,10..35] [0,10,20,30] Prelude> [0,10..35] :: [Double] [0.0,10.0,20.0,30.0,40.0] Prelude> [0,10..34] :: [Double] [0.0,10.0,20.0,30.0] -- roundされてるっぽい??? Prelude> [0,10..35] :: [Float] [0.0,10.0,20.0,30.0,40.0] -- Floatも同様 Prelude> :m Ratio Prelude Ratio> [0,10..35] :: [Rational] [0%1,10%1,20%1,30%1,40%1] -- Rationalも同様
どうやら等差数列の記法からFractionalな型のリストを作ろうとするとおかしなことが起こるようです。
この部分のロジックはどうなってんだろう思ってPreludeのソースを追ってみると、numericEnumFromThenTo という関数に行きつきました。次のような仕様らしいです。
numericEnumFromThenTo :: (Ord a, Fractional a) => a -> a -> a -> [a] numericEnumFromThenTo e1 e2 e3 = takeWhile pred (numericEnumFromThen e1 e2) where mid = (e2 - e1) / 2 pred | e2 > e1 = (<= e3 + mid) | otherwise = (>= e3 + mid)
第三引数から公差の半分過ぎたとこまで含むらしい。気持ち悪っ。
このへんで議論されていますね。誤差対策ぽいけど、それにしたってこの仕様は何とかすべきだと思うなぁ。
減少列
あんまり関係ないけど、上のコードを見て減少列が書けることを初めて知った。
Prelude> [1 .. -3] [] Prelude> [1,0 .. -3] [1,0,-1,-2,-3]