Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 没有本机类型的数字的最有效表示是什么?_Haskell_Functional Programming_Adt - Fatal编程技术网

Haskell 没有本机类型的数字的最有效表示是什么?

Haskell 没有本机类型的数字的最有效表示是什么?,haskell,functional-programming,adt,Haskell,Functional Programming,Adt,在Haskell上,大多数数字类型都是原生的-Int、Float、Word32等。还有一种仅使用ADT的一元自然数的流行表示法-即Peano编码: data Nat = Succ Nat | Zero 这种数据类型虽然优雅,但效率不高。乘法、幂运算、一元数除法都是不切实际的。我的问题是:如果我们没有本地类型可以依赖,那么在Haskell这样的纯函数语言中,最有效的数字表示法是什么?数据类型和各自的算法是什么样的?Leftaroundabout已经对术语“最有效”的模糊性进行了评论,特别是在提到

在Haskell上,大多数数字类型都是原生的-Int、Float、Word32等。还有一种仅使用ADT的一元自然数的流行表示法-即Peano编码:

data Nat = Succ Nat | Zero

这种数据类型虽然优雅,但效率不高。乘法、幂运算、一元数除法都是不切实际的。我的问题是:如果我们没有本地类型可以依赖,那么在Haskell这样的纯函数语言中,最有效的数字表示法是什么?数据类型和各自的算法是什么样的?

Leftaroundabout已经对术语“最有效”的模糊性进行了评论,特别是在提到Haskell和ghc时。如果您真正想问的是,“在支持ADT(代数数据类型)的语言中,使用ADT(代数数据类型)对数字进行更有效的编码可能是什么样子的?”,那么我会向您指出,在这里,您可以练习定义自然数的二进制表示

书中的一段话:

练习:三星(双星)

考虑一种不同的、更有效的自然数表示法 使用二进制而不是一元系统。也就是说,而不是说 每个自然数要么是零,要么是一个自然数的后继数 数字,我们可以说每个二进制数是

  • 零,
  • 一个二进制数的两倍
  • 或者一个二进制数的两倍以上
(a) 首先,写出对应类型bin的归纳定义 这是对二进制数的描述

(提示:回想一下类中nat的定义

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