哈斯克尔范围和浮游物

为什么Haskell范围符号的行为与浮点数不同,而不是整数和字符?

Prelude> [1, 3 .. 10] :: [Int] [1,3,5,7,9] Prelude> [1, 3 .. 10] :: [Float] [1.0,3.0,5.0,7.0,9.0,11.0] Prelude> ['a', 'c' .. 'f'] "ace" 

如果最后一个元素接近上限,我会理解,但这显然不是一个四舍五入的问题。

语法[e1, e2 .. e3]实际上是enumFromThenTo e1 e2 e3语法糖,这是Enumtypes中的一个函数。

Haskell标准定义了它的语义如下:

对于IntIntegertypes,枚举函数具有以下含义:

  • 序列enumFrom e1是列表[e1,e1 + 1,e1 + 2,…]
  • 序列enumFromThen e1 e2是列表[e1,e1 + i,e1 + 2i,…] ,其中增量ie2 − e1 。 增量可以是零或负数。 如果增量为零,则所有列表元素都是相同的。
  • 序列enumFromTo e1 e3是列表[e1,e1 + 1,e1 + 2,…e3] 。 如果e1 > e3则列表为空。
  • 序列enumFromThenTo e1 e2 e3是列表[e1,e1 + i,e1 + 2i,…e3] ,其中增量ie2 − e1 。 如果增量为正数或零,则列表在下一个元素大于e3时终止; 如果e1 > e3则列表为空。 如果增量为负值,则列表在下一个元素小于e3时终止; 如果e1 < e3则列表是空的。

这几乎是你所期望的,但是FloatDouble实例的定义是不同的:

对于FloatDoubleenumFrom族的语义是由上面的Int规则给出的,除了当元素变大于e3 + i∕2对于正增量i )或者当它们变得小于e3 + i∕2时列表终止e3 + i∕2i

我不确定这是什么理由,所以我能给你的唯一答案就是这样,因为它是在标准中定义的。

你可以通过枚举使用整数并转换为Float来解决这个问题。

 Prelude> map fromIntegral [1, 3 .. 10] :: [Float] [1.0,3.0,5.0,7.0,9.0] 

好的,@Henning Makholm已经在他的评论中这样说过了,但是他没有解释为什么这是一个更好的解决scheme。

首先要说的是:在处理浮点时,我们必须时刻注意到可能的舍入错误。 当我们写[0.0, 0.1 .. 1.0]我们必须知道,除了第一个数字之外,所有这些数字都不会在十分之十的位置。 我们需要这种确定性,我们绝对不能使用浮筒。

但是,当然有许多应用程序,我们满意合理的内容,但需要高速。 这就是花车很棒的地方。 这种清单的一个可能的应用是简单的梯形数值积分:

 trIntegrate flrs = sum [ fx | x<-[l,(l+s)..r] ] * s - (f(l)+f(r))*s/2 

让我们testing一下: trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1 => 25.797334337026466
相比之下, 25.9144的误差不到百分之一。 当然不是确切的,但这是整合方法的内在。

现在假设浮动范围被定义为在跨越右边界时始终终止。 那么,这将是可能的(但我们不能确定它),只有20个值而不是21个计算在总和中,因为x的最后一个值恰好是3.000000个东西。 我们可以模拟这个

 bad_trIntegrate flrs = sum [ fx | x<-[l,(l+s)..(rs)] ] * s - (f(l)+f(r))*s/2 

那么我们得到

 bad_trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1 

=> 21.27550564546988
urgh!

这与隐藏浮点的问题无关。 这只是帮助程序员更容易解决这些问题的一种方法。 事实上, [1, 3 .. 10] :: Float的违反直觉的结果有助于记住这些问题!