簡単な答え
この動作を理解するには、式がクラスのメソッドである場所[a,b..c]
に脱糖されることを知っておく必要があります。enumFromThenTo a b c
enumFromThenTo
Enum
Haskell規格は次のように述べています
Float
およびの場合、ファミリDouble
のセマンティクスは上記のルールによって与えられます。ただし、要素が正の増分の場合よりも大きくなるか、負の増分の場合よりも小さくなると、リストは終了します。enumFrom
Int
e3 + i∕2
i
e3 + i∕2
i
結局のところ、標準は標準です。しかし、それはあまり満足のいくものではありません。
深くなる
のDouble
インスタンスはEnum
モジュールGHC.Floatで定義されているので、そこで見てみましょう。我々は気づく:
instance Enum Double where
enumFromThenTo = numericFromThenTo
それは信じられないほど役に立ちませんが、グーグルですばやく検索すると、 GHC.RealnumericFromThenTo
で定義されていることがわかります。そこで行きましょう。
numericEnumFromThenTo e1 e2 e3 = takeWhile pred (numericEnumFromThen e1 e2)
where
mid = (e2 - e1) / 2
pred | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
それは少し良いです。の賢明な定義を仮定するとnumericEnumFromThen
、
numericEnumFromThenTo 0.1 0.3 1.0
結果として
takeWhile pred [0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3 ...]
以来e2 > e1
、の定義pred
は
pred = (<= e3 + mid)
where
mid = (e2 - e1) / 2
x
したがって、を満たす限り、このリストから要素を取得します(それらを呼び出します) x <= e3 + mid
。その値が何であるかGHCiに聞いてみましょう:
>> let (e1, e2, e3) = (0.1, 0.3, 1.0)
>> let mid = (e2 - e1) / 2
>> e3 + mid
1.1
そのため1.09999...
、結果のリストに表示されます。
の1.0999...
代わりに表示される理由は、がバイナリで正確に表現できない1.1
ためです。1.1
推論
なぜ標準はそのような奇妙な振る舞いを規定するのでしょうか?さて、あなたが満足する数だけを取った場合に何が起こるかを考えてください(<= e3)
。浮動小数点エラーまたは表現不能のためにe3
、生成された数値のリストにまったく表示されない場合があります。これは、次のような無害な式を意味する可能性があります。
[0.0,0.02 .. 0.1]
結果として
[0.0, 0.02, 0.04, 0.06, 0.08]
これは少し奇妙に思えます。の修正numericFromThenTo
により、この(おそらくより一般的な)ユースケースで期待される結果が得られることを確認します。