【问题标题】:Termination check of a recursive function call in agdaagda 中递归函数调用的终止检查
【发布时间】:2015-08-21 16:54:14
【问题描述】:

以下代码在 Haskell 中完全没问题:

dh :: Int -> Int -> (Int, Int)
dh d q = (2^d, q^d)
a = dh 2 (fst b)
b = dh 3 (fst a)

Agda 中的类似代码无法编译(终止检查失败):

infixl 9 _^_
_^_ : ℕ → ℕ → ℕ
x ^ zero = 1
x ^ suc n = x * (x ^ n)

dh : ℕ -> ℕ -> ℕ × ℕ
dh d q = 2 ^ d , q ^ d

mutual
  a = dh 2 (proj₁ b)
  b = dh 3 (proj₁ a)

a 的定义使用 a,它在结构上并不更小,因此是循环。似乎终止检查器不会查看 dh 的定义。

我尝试过使用 coduction,设置选项 --termination-depth=4 -- 没有帮助。 在mutual 块中插入{-# NO_TERMINATION_CHECK #-} 会有所帮助,但它看起来像是作弊。

有没有一种干净的方法可以让 Agda 编译代码? Agda 的终止检查器是否有一些基本限制?

【问题讨论】:

    标签: recursion agda termination coinduction


    【解决方案1】:

    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 后端会为 ab 生成不会终止的代码,因为 JavaScript 是经过严格评估的。

    Agda's termination checker 检查强规范化程序的子集:那些只有结构递归的程序。它查看函数定义中与模式匹配的构造函数的数量和顺序,以确定递归调用是否使用“较小”的参数。 ab 没有任何参数,因此终止检查器认为从 aa(通过 b)的嵌套调用与 a 本身的“大小”相同。

    【讨论】:

    • 我对 corecursion 还不太熟悉,所以我没有评论它是否可以在这里为您所用。
    • 我同意你的看法。但是从另一个角度来看dh的结果只是一对s,ab的求值可以分解为:aQ = 2 ^ ad ; bQ = 2 ^ bd ; abQ = bQ ^ ad ; baQ = aQ ^ bdmutual 关键字甚至不需要。 dh 的重点是封装算法的步骤并抽象评估顺序。分解后的代码变得过于冗长。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 2012-09-03
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    • 2013-01-19
    • 2013-11-30
    相关资源
    最近更新 更多