【问题标题】:Rationale behind Haskells `succ` on numbers (floats)Haskells `succ` 关于数字的基本原理(浮点数)
【发布时间】:2015-05-05 13:30:39
【问题描述】:

得知 Haskell 将其在数字上的 succ 函数定义为加一时,我有点惊讶:

succ :: a -> a
一个值的继承者。对于数字类型,succ 添加 1

虽然对于整数值,这似乎是合理的,但存在一些问题:

  • 如果您定义一个只能表示偶数/奇数/素数/...数字的数字系统,换句话说,一个专用类型是整数的子集;
  • 如果您定义某种表示半数和全数的“定点数”,在这种情况下,不会枚举所有数字;和
  • 浮点数的问题最为严重。

首先,这意味着[2.0 :: Float .. 3.0 :: Float](使用:: Float 来确保调用没有歧义)仅包含添加到原始值的整数值,而如果使用此表达式,他/她可能会期望列表将包括两个值之间的所有浮点数;当然,这个论点更多的是关于一个人的喜好。大多数程序员在这方面没有太多问题。

更严重的是,如果使用表达式 [2.2 :: Float .. 4.0 :: Float] 会导致 [2.2,3.2,4.2] 4.2 在这里做什么?

如果使用浮点数,而+1 无法生成不同的数字(因为尾数没有足够的位来表示一个),它将无限循环。例如:

Prelude> [1e37 :: Float .. 1e37 :: Float]
[1.0e37,1.0e37,1.0e37,1.0e37,1.0e37,1^C.0e37,Interrupted.
Prelude> [1e37 :: Float .. 1e37-1 :: Float]
[1.0e37,1.0e37,1.0e37,1.0e37,1.0e37,1^C.0e37,Interrupted.
Prelude> [1e37 :: Float .. 1e37+1 :: Float]
[1.0e37,1.0e37,1.0e37,1.0e37,1.0e37,1^C.0e37,Interrupted.
Prelude> [1e37 :: Float .. pred 1e37 :: Float]
[1.0e37,1.0e37,1.0e37,1.0e37,1.0e37,1^C.0e37,Interrupted.

因此,即使列表应该为空或包含一些元素,也会生成无限数量的值。

部分论点确实有点吹毛求疵,但至少对某些人来说,假设 Haskell 程序员最终会犯错误是合理的。

难道不是更合理的方法是生成下一个可表示的浮点数吗?

以这种方式定义succ 的论据是什么? FloatEnum 的实例是否合理?

【问题讨论】:

  • 浮点 [x..y] 间隔在恕我直言中存在固有缺陷——没有合理的方法来定义它们。列出xy 之间的所有浮点数会很有趣,但在实践中并不是很有用(多久会使用一次?)。正如我们现在所拥有的一步式列表会暴露于舍入错误,如果由于舍入错误,1+1+... 恰好是 100+epsilon,则会导致 [1.0 .. 100.0]99 结尾(它不会,但如果我们替换 100有足够大的数字..)。 Haskell 采用的“解决方案”将4.2 视为4.0+epsilon,因此将其包含在列表中。 (epsilon~0.5)
  • @chi:是的,我认为最后一个方面(4.2=4+e)是有问题的部分。正如问题中所说,它迭代添加积分并没有那么糟糕。但是人们会期望范围在限制之前(或在)结束。 Float 可能不是 Enuminstance 可能会更好......毕竟人们仍然可以用 map (\x -> fromIntegral x + 0.2) [0..4] 模拟它......
  • @chi:列出一个范围内的所有浮点数非常有用,至少对于 32 位浮点数来说是这样。例如,您可以针对某个范围内的所有浮点数测试函数的正确性。
  • @tmyklebu 好的,但你认为在实践中会如此普遍地使用它,值得一个特殊的语法[x..y] 吗?相反,我可以看到它可以作为库函数使用。
  • @CommuSoft 不要误会我的意思——我可以看到选择floor(y-x) 会导致有用的结果(至少可以保证介于两个极端之间)。 Haskell 委员会粗略地选择了round(y-x),可能担心[1.0 .. x] 其中x 被评估为10.0 有一些非常小的错误应该正好有10 元素。

标签: haskell floating-point enumerable


【解决方案1】:

succ 函数本身的由来其实和 Haskell 的数据类型或枚举无关,实际上是 succ 函数先来的。 succ 函数实际上是axiom of infinity 中的successor 函数,它允许我们create numbers in the first place。它从未设计用于浮点/非自然数,这就是您遇到此问题的原因。

在 Haskell 中为浮点类型修改 succ 函数可能是个好主意,你应该向邮件列表提交一些关于它的内容。虽然 Haskell 在 Haskell98 报告中是标准化的,所以不要对改变语言抱有希望。

如果您熟悉 Haskell 的类型类,请阅读以下内容: 您提到了 succ 函数的不同可能用途,这就是为什么它被定义为 Enum 类型的函数班级。因此,您可以轻松地重新绑定它以使用 newtype 执行不同的操作。

【讨论】:

  • 我确实熟悉 Haskell 的类型类,使用 FloatEnum 类型类确实是合理的,尽管 Enum 用于许多实用程序类中,因此人们会期望标准类要非常小心。
  • @CommuSoft 我完全同意这种理性,这就是为什么我鼓励你向其中一个邮件列表提交一些东西。
  • 可能值得一提的是,succ 在类型论基础以及自然数的类型论定义中可以有一个相似到相同的后代。后者对于 Haskell 这样的系统可能更自然。
  • @J.Abrahamson 可能,虽然我对类型理论不够熟悉,无法自信地使用它发布答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多