无法确定终止

时间:2018-01-16 20:12:38

标签: coq termination totality

用于确定集合是否是另一个集合的子集的函数:

Fixpoint subset (s1:bag) (s2:bag) : bool :=
  match s1 with
  | nil => true
  | h :: t => match (beq_nat (count h s1) (count h s2)) with
    | true => subset (remove_all h t) (remove_all h s2)
    | false => false
    end
  end.

为清楚起见

  • beq_nat确定两个自然数相等
  • count计算给定自然数在集合
  • 中出现的次数
  • remove_all从集合
  • 中删除给定自然数的每个实例

CoqIDE"无法猜测修正的减少论点。"鉴于递归是在t的一个子集(s1的尾部)上进行的,为什么不能保证终止?

注意:此问题来自this website,其作者请求解决方案不公开发布。此外,我已经解决了这个问题,所以不需要解决方案。我们非常感谢coq无法确定终止的原因。

2 个答案:

答案 0 :(得分:5)

作为第一个近似值,接受递归调用的规则是,在递归调用中,其中一个参数应该是通过模式匹配变量获得的变量输入中相同等级的输入变量。实际上,规则略微放松,但并不多。

这是一个实例:

Fixpoint plus (n m : nat) : nat :=
  match n with
  | O => m
  | S p => S (plus p m)
  end.

接受的解释是p是等级1的参数,它是作为n的模式匹配变量获得的,它是等级1的初始参数。所以函数是结构递归,减少第一个参数。应该总是有一个减少的论点。不接受多个论点之间的综合减少。

如果你不想被淹死,你应该在这里停止阅读。

规则的第一个放松是减少的递归参数可以是模式匹配构造,只要所有分支中的值确实是小于第一个的变量。这是一个利用这个想法的尴尬功能的例子:

Require Import List Arith.

Fixpoint awk1 (l : list nat) :=
  match l with
  | a :: ((b :: l'') as l') => 
    b :: awk1 (if Nat.even a then l' else l'')
  | _ => l
  end.

因此在函数awk1中,递归调用不是在变量上,而是在模式匹配表达式上,但它没关系,因为这个递归调用的所有可能值确实是通过模式匹配获得的变量。这也说明了终止检查程序的挑剔程度,因为不接受表达式(if Nat.even a then (b :: l'') else l'')(b :: l'')不是变量。

规则的第二个放松是递归参数可以是函数调用,只要此函数调用可转换为可接受的表达式。这是一个例子,跟进前一个。

Definition arg n (l : list nat) :=
  if Nat.even n then
    l 
  else
    match l with _ :: l' => l' | _ => l end.

Fixpoint awk2 (l : list nat) :=
match l with
  a :: l' => a :: awk2 (arg a l')
| _ => l
end.

规则的第三个放松是用于计算递归参数的函数甚至可以是递归的,只要它可以递归地传递递减属性。这是一个例子:

Fixpoint mydiv (n : nat) (m : nat) :=
   match n, m with
     S n', S m' => S (mydiv (Nat.sub n' m') m)
   | _, _ => n
   end.

如果打印Nat.sub的定义,您将看到它经过精心设计,始终返回递归调用的结果或第一个输入,而且,在递归调用中,第一个参数确实是通过第一个输入的模式匹配获得的变量。这种不断下降的特性得到了认可。

答案 1 :(得分:2)

你的终止论证是正确的,但是Coq并不聪明,不能自己解决这个问题。粗略地说,Coq只接受对其主要参数的句法子句执行的递归调用。这是一个非常严格的概念:例如,[1; 3][0; 1; 2; 3]的子列表,但不是语法子项。

如果你想让Coq接受这个,你可能需要使用有根据的递归来重写你的函数。 Adam Chipala的书CPDT有nice chapter on this