作为第一个近似值,接受递归调用的规则是,在递归调用中,其中一个参数应该是通过模式匹配从输入中相同等级的输入变量获得的变量。实际上,该规则稍微宽松一些,但幅度不大。
这是一个例子:
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
您会发现它经过精心设计,始终返回递归调用的结果或第一个输入,此外,在递归调用中,第一个参数确实是通过模式匹配获得的变量第一个输入。这种递减性质是公认的。