let rec length #a (l:list a) : nat = match l with | [] -> 0 | _ :: tl -> 1 + length tl
递归实现将一个 list 与另一个 list 合并:
1 2 3 4 5
let rec append #a (l1 l2:list a) : list a = match l1 with | [] -> l2 | hd :: tl -> hd :: append tl l2
递归实现将列表根据大于或小于等于 pivot 分成两半,
注意在函数签名中指定该函数不改变列表总长,
这样在排序时可以让 SMT Prover 相信递归会结束。
1 2 3 4 5 6 7 8 9
let rec partition (#a:Type) (f:a -> bool) (l:list a) : x:(list a & list a) { length (fst x) + length (snd x) = length l } = match l with | [] -> [], [] | hd::tl -> let l1, l2 = partition f tl in if f hd then hd::l1, l2 else l1, hd::l2
递归实现排序:
1 2 3 4 5 6 7
let rec sort (l:list int) : Tot (list int) (decreases (length l)) = match l with | [] -> [] | pivot :: tl -> let hi, lo = partition ((<=) pivot) tl in append (sort lo) (pivot :: sort hi)
//SNIPPET_START: partition let rec partition (#a:Type) (f:a -> bool) (l:list a) : x:(list a & list a) { length (fst x) + length (snd x) = length l } = match l with | [] -> [], [] | hd::tl -> let l1, l2 = partition f tl in if f hd then hd::l1, l2 else l1, hd::l2 //SNIPPET_END: partition
//SNIPPET_START: sort-impl let rec sort #a (f:total_order_t a) (l:list a) : Tot (list a) (decreases (length l)) = match l with | [] -> [] | pivot :: tl -> let hi, lo = partition (f pivot) tl in append (sort f lo) (pivot :: sort f hi) //SNIPPET_END: sort-impl
//SNIPPET_START: partition_mem let rec partition_mem (#a:eqtype) (f:(a -> bool)) (l:list a) : Lemma (let l1, l2 = partition f l in (forall x. mem x l1 ==> f x) /\ (forall x. mem x l2 ==> not (f x)) /\ (forall x. mem x l = (mem x l1 || mem x l2))) = match l with | [] -> () | hd :: tl -> partition_mem f tl //SNIPPET_END: partition_mem
//SNIPPET_START: sorted_concat let rec sorted_concat (#a:eqtype) (f:total_order_t a) (l1:list a{sorted f l1}) (l2:list a{sorted f l2}) (pivot:a) : Lemma (requires (forall y. mem y l1 ==> not (f pivot y)) /\ (forall y. mem y l2 ==> f pivot y)) (ensures sorted f (append l1 (pivot :: l2))) = match l1 with | [] -> () | hd :: tl -> sorted_concat f tl l2 pivot //SNIPPET_END: sorted_concat
//SNIPPET_START: append_mem let rec append_mem (#t:eqtype) (l1 l2:list t) : Lemma (ensures (forall a. mem a (append l1 l2) = (mem a l1 || mem a l2))) = match l1 with | [] -> () | hd::tl -> append_mem tl l2 //SNIPPET_END: append_mem
let rec sort_correct (#a:eqtype) (f:total_order_t a) (l:list a) : Lemma (ensures ( let m = sort f l in sorted f m /\ (forall i. mem i l = mem i m))) (decreases (length l)) = match l with | [] -> () | pivot :: tl -> let hi, lo = partition (f pivot) tl in sort_correct f hi; sort_correct f lo; partition_mem (f pivot) tl; sorted_concat f (sort f lo) (sort f hi) pivot; append_mem (sort f lo) (pivot :: sort f hi)
let rec sort_intrinsic (#a:eqtype) (f:total_order_t a) (l:list a) : Tot (m:list a { sorted f m /\ (forall i. mem i l = mem i m) }) (decreases (length l)) = match l with | [] -> [] | pivot :: tl -> let hi, lo = partition (fun x -> f pivot x) tl in partition_mem (fun x -> f pivot x) tl; sorted_concat f (sort_intrinsic f lo) (sort_intrinsic f hi) pivot; append_mem (sort_intrinsic f lo) (pivot :: sort_intrinsic f hi); partition_mem_permutation append (sort_intrinsic f lo) (pivot :: sort_intrinsic f hi)
//Some auxiliary definitions to make this a standalone example let rec length #a (l:list a) : nat = match l with | [] -> 0 | _ :: tl -> 1 + length tl
let rec append #a (l1 l2:list a) : list a = match l1 with | [] -> l2 | hd :: tl -> hd :: append tl l2
let total_order (#a:Type) (f: (a -> a -> bool)) = (forall a. f a a) (* reflexivity *) /\ (forall a1 a2. (f a1 a2 /\ a1=!=a2) <==> not (f a2 a1)) (* anti-symmetry *) /\ (forall a1 a2 a3. f a1 a2 /\ f a2 a3 ==> f a1 a3) (* transitivity *) /\ (forall a1 a2. f a1 a2 \/ f a2 a1) (* totality *) let total_order_t (a:Type) = f:(a -> a -> bool) { total_order f }
let rec sorted #a (f:total_order_t a) (l:list a) : bool = match l with | [] -> true | [x] -> true | x :: y :: xs -> f x y && sorted f (y :: xs)
//SNIPPET_START: count permutation let rec count (#a:eqtype) (x:a) (l:list a) : nat = match l with | hd::tl -> (if hd = x then 1 else 0) + count x tl | [] -> 0
let mem (#a:eqtype) (i:a) (l:list a) : bool = count i l > 0
let is_permutation (#a:eqtype) (l m:list a) = forall x. count x l = count x m
let rec append_count (#t:eqtype) (l1 l2:list t) : Lemma (ensures (forall a. count a (append l1 l2) = (count a l1 + count a l2))) = match l1 with | [] -> () | hd::tl -> append_count tl l2 //SNIPPET_END: count permutation
let rec partition (#a:Type) (f:a -> bool) (l:list a) : x:(list a & list a) { length (fst x) + length (snd x) = length l } = match l with | [] -> [], [] | hd::tl -> let l1, l2 = partition f tl in if f hd then hd::l1, l2 else l1, hd::l2
let rec sort #a (f:total_order_t a) (l:list a) : Tot (list a) (decreases (length l)) = match l with | [] -> [] | pivot :: tl -> let hi, lo = partition (f pivot) tl in append (sort f lo) (pivot :: sort f hi)
let rec partition_mem_permutation (#a:eqtype) (f:(a -> bool)) (l:list a) : Lemma (let l1, l2 = partition f l in (forall x. mem x l1 ==> f x) /\ (forall x. mem x l2 ==> not (f x)) /\ (is_permutation l (append l1 l2))) = match l with | [] -> () | hd :: tl -> let l1, l2 = partition f tl in append_count l1 l2; partition_mem_permutation f tl
let rec sorted_concat (#a:eqtype) (f:total_order_t a) (l1:list a{sorted f l1}) (l2:list a{sorted f l2}) (pivot:a) : Lemma (requires (forall y. mem y l1 ==> not (f pivot y)) /\ (forall y. mem y l2 ==> f pivot y)) (ensures sorted f (append l1 (pivot :: l2))) = match l1 with | [] -> () | hd :: tl -> sorted_concat f tl l2 pivot
let permutation_app_lemma (#a:eqtype) (hd:a) (tl:list a) (l1:list a) (l2:list a) : Lemma (requires (is_permutation tl (append l1 l2))) (ensures (is_permutation (hd::tl) (append l1 (hd::l2)))) = append_count l1 l2; append_count l1 (hd::l2)
let rec sort_correct (#a:eqtype) (f:total_order_t a) (l:list a) : Lemma (ensures ( sorted f (sort f l) /\ is_permutation l (sort f l))) (decreases (length l)) = match l with | [] -> () | pivot :: tl -> let hi, lo = partition (f pivot) tl in sort_correct f hi; sort_correct f lo; partition_mem_permutation (f pivot) tl; sorted_concat f (sort f lo) (sort f hi) pivot; append_count hi lo; permutation_app_lemma pivot tl (sort f hi) (sort f lo)
let rec sort_intrinsic (#a:eqtype) (f:total_order_t a) (l:list a) : Tot (m:list a { sorted f m /\ is_permutation l m }) (decreases (length l)) = match l with | [] -> [] | pivot :: tl -> let hi, lo = partition (fun x -> f pivot x) tl in partition_mem_permutation (fun x -> f pivot x) tl; sorted_concat f (sort_intrinsic f lo) (sort_intrinsic f hi) pivot; append_count hi lo; permutation_app_lemma pivot tl (sort f hi) (sort f lo); append (sort_intrinsic f lo) (pivot :: sort_intrinsic f hi)