Recursion agda中递归函数调用的终止检查
以下代码在Haskell中完全可以使用:Recursion agda中递归函数调用的终止检查,recursion,agda,termination,coinduction,Recursion,Agda,Termination,Coinduction,以下代码在Haskell中完全可以使用: dh :: Int -> Int -> (Int, Int) dh d q = (2^d, q^d) a = dh 2 (fst b) b = dh 3 (fst a) Agda中的类似代码无法编译(终止检查失败): a的定义使用了a,它在结构上不是更小的,因此是循环。似乎终止检查器不会查看dh的定义 我尝试过使用coinduction,设置选项——终止深度=4——没有帮助。 在mutual块中插入{-#NO#u TERMINATION_C
dh :: Int -> Int -> (Int, Int)
dh d q = (2^d, q^d)
a = dh 2 (fst b)
b = dh 3 (fst a)
Agda中的类似代码无法编译(终止检查失败):
a
的定义使用了a
,它在结构上不是更小的,因此是循环。似乎终止检查器不会查看dh
的定义
我尝试过使用coinduction,设置选项——终止深度=4
——没有帮助。
在mutual
块中插入{-#NO#u TERMINATION_CHECK#-}
会有所帮助,但看起来像是作弊
有没有一种干净的方法让Agda编译代码?Agda的终止检查程序是否有一些基本的限制?Agda不像Haskell那样假设延迟评估。相反,Agda要求所有表达式都进行强规范化。这意味着您应该得到相同的答案,而不管您计算子表达式的顺序如何。您给出的定义不是严格规范化的,因为存在一个不会终止的求值顺序:
a
-->
dh 2 (proj₁ b)
-->
dh 2 (proj₁ (dh 3 (proj₁ a))
-->
dh 2 (proj₁ (dh 3 (proj₁ (dh 2 (proj₁ b)))))
特别是,Agda的JavaScript后端将生成不会终止于a
和b
的代码,因为JavaScript是经过严格评估的
检查强规范化程序的子集:那些只有结构递归的程序。它查看函数定义中匹配的构造函数模式的数量和顺序,以确定递归调用是否使用“较小”的参数
a
和b
没有任何参数,因此终止检查器将从a
到a
(通过b
)的嵌套调用视为相同的“大小”作为a
本身。我还不太熟悉corecursion,所以我没有评论它是否可以用于您的优势。我同意您的看法。但是从另一个角度来看,dh
的结果只是一对ℕ
s,对a
和b
的评估可以分解为:aQ=2^ad;bQ=2^bd;abQ=bQ^ad;baQ=aQ^bd
。甚至不需要mutual
关键字。dh
的要点是增加算法的步骤,并抽象出计算顺序。在分解形式中,代码变得过于冗长。
a
-->
dh 2 (proj₁ b)
-->
dh 2 (proj₁ (dh 3 (proj₁ a))
-->
dh 2 (proj₁ (dh 3 (proj₁ (dh 2 (proj₁ b)))))