我想计算两个Natural的商。我需要满足的要求是,我有几个配置项必须动态地定义为另一个容器的共享(即一个容器具有X内存,该容器中的两个进程配置键被定义为X / Y和X / Z)。
我的第一个想法是使用递归,但这种方法没有奏效:
let quotient =
\(n: Natural) ->
\(m: Natural) ->
if lessThan n m then 0
else 1 + quotient (Natural/subtract m n) m特别是,Dhall抱怨说,当我尝试调用quotient时,还没有定义它。考虑到Dhall的全部功能范式(以及我对它的不熟悉),这似乎是合理的。我想也许有什么办法可以做到,但不幸的是,我没能做到。
我使用Natural/fold进行了另一次尝试,但我不确定这是否有意义。
let quotient =
λ(n : Natural) →
λ(m : Natural) →
let div =
λ(n : Natural) →
λ(m : Natural) →
let Div = { q : Natural, r : Natural }
in Natural/fold
n
Div
( λ(d : Div) →
if Natural/isZero m
then d
else if lessThan d.r m
then d
else { q = d.q + 1, r = Natural/subtract m d.r }
)
{ q = 0, r = n }
in (div n m).q这传递了以下所有断言。
let example1 = assert : quotient 1 1 ≡ 1
let example2 = assert : quotient 2 2 ≡ 1
let example3 = assert : quotient 3 1 ≡ 3
let example4 = assert : quotient 3 2 ≡ 1
let example5 = assert : quotient 9 4 ≡ 2
let example6 = assert : quotient 4 5 ≡ 0
let example7 = assert : quotient 0 1 ≡ 0
let example8 = assert : quotient 0 2 ≡ 0
let example9 = assert : quotient 1 0 ≡ 0
let example9 = assert : quotient 0 0 ≡ 0
let example9 = assert : quotient 2 0 ≡ 0在我的情况下,当除以0时返回0是可以的。
有什么更地道的方法来实现这一点呢?我在Prelude中寻找一个现成的整数除法函数,但是找不到它。
发布于 2020-10-01 04:39:55
目前,除了您已经编写的内容之外,没有一种更简单的实现Natural部门的方法,但是可能有一种更有效的方法。另一种方法是“猜测和检查”,它会给出对数时间复杂度,但实现却要复杂得多,您可以围绕所需的数字进行二进制搜索,以找到最大的数字x,比如x * m = n。
不过,我并不是真的建议这样做,我认为更好的方法可能是看看是否有一个合理的内置语言来增加能够有效地驱动整数除法的语言。理想情况下,对于所有输入都可以很好地定义这种内建,因此直接添加整数除法可能不起作用(因为x / 0没有很好地定义)。然而(我在这里说的是),也许像Natural/safeDivide x y == x / (y + 1)这样的内置程序可以工作,然后用户可以定义他们自己的包装,如果他们想要允许按0划分。或许,征求意见的最佳场所是讨论论坛:
发布于 2020-10-01 13:20:54
TL;WR编辑:
let Natural/div = λ(n : Natural) → λ(m : Natural) →
let div = https://github.com/jcaesar/dhall-div/releases/download/1/quotient.dhall sha256:d6a994f4b431081e877a0beac02f5dcc98f3ea5b027986114487578056cb3db9
in (div n m).q加布里埃尔·冈萨雷斯( Gabriel Gonzalez )用他的answer提到了二进制搜索,让我非常讨厌。有一段时间,我在圈内运行,试图通过将数字转换为List Bool来实现搜索所需的两个除法(可以,问题如下所示),然后我注意到您可以只实现长除法:
let Natural/le = λ(a : Natural) → λ(b : Natural) → Natural/isZero (Natural/subtract b a)
let Natural/equals = λ(a : Natural) → λ(b : Natural) → Natural/le a b && Natural/le b a
let bits =
λ(bits : Natural) →
Natural/fold
bits
(List Natural)
( λ(l : List Natural) →
l
# [ merge
{ Some = λ(i : Natural) → i * 2, None = 1 }
(List/last Natural l)
]
)
([] : List Natural)
in λ(w : Natural) →
let bits = bits w
in λ(n : Natural) →
λ(m : Natural) →
let T = { r : Natural, q : Natural } : Type
let div =
List/fold
Natural
bits
T
( λ(bit : Natural) →
λ(t : T) →
let m = m * bit
in if Natural/le m t.r
then { r = Natural/subtract m t.r, q = t.q + bit }
else t
)
{ r = n, q = 0 }
in if Natural/equals m 0 then 0 else div.q唯一的问题是,由于没有对数,您不能在表中对长除法做左对齐,也就是说,您不知道MSB在n中的位置或subs的长度。
我的理论家很伤心,因为我刚刚把除法降到了对数的粗略近似,但是医生说:“你总是对u64很满意,闭嘴。”
编辑经过一点思考,我仍然不能有效地计算对数和所有输入(我认为这是不可能的)。但是,我可以从一个固定的大极限(2^2^23或42×10^2525221,但见下文)的对数中找到下一个2的幂。可以通过以下方式修改上述函数(让我们将其称为quotient):
let Natural/less =
λ(a : Natural) →
λ(b : Natural) →
if Natural/isZero (Natural/subtract a b) then False else True
let max = 23
let powpowT = { l : Natural, n : Natural }
let powpow =
Natural/fold
max
(List powpowT)
( λ(ts : List powpowT) →
merge
{ Some =
λ(t : powpowT) → [ { l = t.l + t.l, n = t.n * t.n } ] # ts
, None = [ { l = 1, n = 2 } ]
}
(List/head powpowT ts)
)
([] : List powpowT)
let powpow = List/reverse powpowT powpow
let bitapprox =
λ(n : Natural) →
List/fold
powpowT
powpow
Natural
( λ(e : powpowT) →
λ(l : Natural) →
if Natural/less n e.n then e.l else l
)
0
in λ(n : Natural) → λ(m : Natural) → quotient (bitapprox n) n m这提供了一个可接受的有效实现商,一个长的除法表,这是最大的两倍的必要。在我的桌面(62 in ) m上,我可以在11秒内计算出例如2^(2^18) /7,但是对于更大的数字,内存不足。
不管怎么说,如果你有这么大的数字,你使用错误的语言。
https://stackoverflow.com/questions/64116125
复制相似问题