Haskell 没有本机类型的数字的最有效表示是什么?
在Haskell上,大多数数字类型都是原生的-Int、Float、Word32等。还有一种仅使用ADT的一元自然数的流行表示法-即Peano编码:Haskell 没有本机类型的数字的最有效表示是什么?,haskell,functional-programming,adt,Haskell,Functional Programming,Adt,在Haskell上,大多数数字类型都是原生的-Int、Float、Word32等。还有一种仅使用ADT的一元自然数的流行表示法-即Peano编码: data Nat = Succ Nat | Zero 这种数据类型虽然优雅,但效率不高。乘法、幂运算、一元数除法都是不切实际的。我的问题是:如果我们没有本地类型可以依赖,那么在Haskell这样的纯函数语言中,最有效的数字表示法是什么?数据类型和各自的算法是什么样的?Leftaroundabout已经对术语“最有效”的模糊性进行了评论,特别是在提到
data Nat = Succ Nat | Zero
这种数据类型虽然优雅,但效率不高。乘法、幂运算、一元数除法都是不切实际的。我的问题是:如果我们没有本地类型可以依赖,那么在Haskell这样的纯函数语言中,最有效的数字表示法是什么?数据类型和各自的算法是什么样的?Leftaroundabout已经对术语“最有效”的模糊性进行了评论,特别是在提到Haskell和ghc时。如果您真正想问的是,“在支持ADT(代数数据类型)的语言中,使用ADT(代数数据类型)对数字进行更有效的编码可能是什么样子的?”,那么我会向您指出,在这里,您可以练习定义自然数的二进制表示 书中的一段话: 练习:三星(双星) 考虑一种不同的、更有效的自然数表示法 使用二进制而不是一元系统。也就是说,而不是说 每个自然数要么是零,要么是一个自然数的后继数 数字,我们可以说每个二进制数是
- 零,
- 一个二进制数的两倍
- 或者一个二进制数的两倍以上
Inductive nat : Type :=
| O : nat
| S : nat → nat.
正如@Cirdec已经评论的那样,这个问题对于编程语言没有意义,它只对特定的编程问题有意义
例如,如果你需要精确的数字,你可能想用连分数来表示有理数。这对于簿记来说可能是次优的。这在很大程度上取决于你想用数字做什么,以及什么是最有效的 如果你想表示一个自然数n,你需要logn位的信息。由于一个ADT只能有有限多个不同的构造函数,它编码有限数量的位,所以你需要至少有logn个节点的结构 我非常推荐Chris Okasaki(该论文在线提供)中的数字表示章节。它描述了各种树状数据结构,支持不同的运算集,以及它们与自然数的关系。下面的内容是我从这本书中学到的 扩展Cirdec的评论:您可以定义
data N = Zero | Positive
data Positive = One | Add Positive Positive
这就得到了O(1)的加法和减法。另一方面,结构的大小将是O(n)
您可以使用带有O(logn)空间的二进制表示法,但随后的加法将是O(logn):
增量和减量几乎都是O(1)。一系列增量只会在每2^d次操作中到达深度d,因此平均而言,每个增量都是O(1)。类似于decerements。我在上面说过几乎,因为如果你交换增量和减量,那么你可以在O(logn)之间切换递增和递减操作。解决方法是添加一些冗余:
data N = Zero | Positive
data Positive = One | Twice Positive | TwicePlusOne Positive | TwicePlusTwo Positive
现在,每次操作需要更深一层时,它都会将当前节点留在TwicePlusOne
,这意味着影响节点的下一个操作将在该节点处停止,而不管它是递增还是递减
如果您想要恒定时间加法,则可以为此扩展数据结构(在书中查找具有有效链接的列表),但您也可以使用O(n)结束内存,如果操作顺序不好。有一个开放的SO问题,询问是否可以同时获得这两个值。两个注释:1.我知道一个明显的解决方案是将数字表示为布尔值列表,并模拟浮点/整数算法-但这是最有效的吗?2.我想这个问题有点广泛,完整的答案是可能的太长了,所以很明显我没有要求完整的实现。只要关键的见解和指针就好了。“最有效”在什么方面?我不认为有任何方法可以给出一个普遍的客观标准,所以或多或少可以归结为“GHC中最有效的是什么”。但答案是,任何编译为标准本机算术的等价物。非常确定,这在不显式使用本机类型的情况下都是可能的。因此,虽然我喜欢这个问题的想法,但我认为没有合适的答案。这在很大程度上取决于您正在执行的操作。数据类型
data N=OnePlusSum N N | Zero
可以非常好地解决一些问题:它对N+1
、N+m+1
以及检查数字是否非零都有O(1)
操作。如果需要除以2或减去1,它就做不好。
data N = Zero | Positive
data Positive = One | Twice Positive | TwicePlusOne Positive | TwicePlusTwo Positive