Recursion Dhall中的整数除法
我想计算两个Recursion Dhall中的整数除法,recursion,functional-programming,division,integer-division,dhall,Recursion,Functional Programming,Division,Integer Division,Dhall,我想计算两个Naturals的商。我需要满足的要求是,我有几个配置项必须动态定义为另一个的共享(即,一个容器具有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 特别是,Dhal
Natural
s的商。我需要满足的要求是,我有几个配置项必须动态定义为另一个的共享(即,一个容器具有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抱怨当我试图调用它时,商
还没有定义。鉴于达尔的总体功能范式(以及我对它的不熟悉),这似乎是合理的。我想可能有办法,但不幸的是我没能做到
我用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
中查找了一个现成的整数除法函数,但找不到它。除了您已经编写的以外,目前没有一种更简单的方法来实现自然的
除法,但可能有一种更有效的方法。另一种方法会给出对数时间复杂度,但实现要复杂得多,这就是“猜测和检查”,即在所需数字周围进行二进制搜索,以找到最大的数字x
,从而x*m=n
我不建议这样做,但我认为可能更好的方法是,看看是否有一个合理的内置程序添加到语言中,可以有效地增强整数除法。理想情况下,这样一个内置函数将为所有输入定义良好,因此直接添加整数除法可能不起作用(因为x/0
没有定义良好)。然而(我在这里吐痰),也许内置的Natural/safedividexy==x/(y+1)
可以工作,然后用户可以定义自己的包装器,如果他们想允许按0
进行划分的话。对于内置的外观,征求意见的最佳场所可能是话语论坛:
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)用他提到的二进制搜索把我这个书呆子狠狠地揍了一顿。有一段时间,我在兜圈子,试着通过将数字转换为
List Bool
(可以,问题与下面相同)来实现搜索所需的2除,然后我注意到您可以实现长除:
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^2^23或42×10^2525221,但见下文)。可以通过以下方式修改上述函数(我们称之为
商
):
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
这提供了一个可接受的商的有效实现,具有一个长除法表,最大为所需除法表的两倍。在我的桌面(62GB)m上,我可以在11秒内计算2^(2^18)/7,但对于较大的数字,内存不足
无论如何,如果你对这么大的数字有意见,那你就用错了语言。Hm,如果
Natural/subtract 3 1===0
,那么为什么不Natural/divide x 0==0
?我只是用上面的实现尝试将2^30除以6,显然内存不足。所以我很好奇:您将如何实现二进制搜索?你不需要除以2和对数吗(我想我可以用Natural/show+字符串长度来近似对数,