700 However, the \cmd{function} package does support partiality to a |
700 However, the \cmd{function} package does support partiality to a |
701 certain extent. Let's look at the following function which looks |
701 certain extent. Let's look at the following function which looks |
702 for a zero of a given function f. |
702 for a zero of a given function f. |
703 *} |
703 *} |
704 |
704 |
705 function (*<*)(domintros, tailrec)(*>*)findzero :: "(nat \<Rightarrow> nat) \<Rightarrow> nat \<Rightarrow> nat" |
705 function (*<*)(domintros)(*>*)findzero :: "(nat \<Rightarrow> nat) \<Rightarrow> nat \<Rightarrow> nat" |
706 where |
706 where |
707 "findzero f n = (if f n = 0 then n else findzero f (Suc n))" |
707 "findzero f n = (if f n = 0 then n else findzero f (Suc n))" |
708 by pat_completeness auto |
708 by pat_completeness auto |
709 (*<*)declare findzero.simps[simp del](*>*) |
709 (*<*)declare findzero.simps[simp del](*>*) |
710 |
710 |
957 lemmas which are occasionally useful are @{text accpI}, @{text |
957 lemmas which are occasionally useful are @{text accpI}, @{text |
958 accp_downward}, and of course the introduction and elimination rules |
958 accp_downward}, and of course the introduction and elimination rules |
959 for the recursion relation @{text "findzero.intros"} and @{text "findzero.cases"}. |
959 for the recursion relation @{text "findzero.intros"} and @{text "findzero.cases"}. |
960 *} |
960 *} |
961 |
961 |
962 (*lemma findzero_nicer_domintros: |
|
963 "f x = 0 \<Longrightarrow> findzero_dom (f, x)" |
|
964 "findzero_dom (f, Suc x) \<Longrightarrow> findzero_dom (f, x)" |
|
965 by (rule accpI, erule findzero_rel.cases, auto)+ |
|
966 *) |
|
967 |
|
968 subsection {* A Useful Special Case: Tail recursion *} |
|
969 |
|
970 text {* |
|
971 The domain predicate is our trick that allows us to model partiality |
|
972 in a world of total functions. The downside of this is that we have |
|
973 to carry it around all the time. The termination proof above allowed |
|
974 us to replace the abstract @{term "findzero_dom (f, n)"} by the more |
|
975 concrete @{term "(x \<ge> n \<and> f x = (0::nat))"}, but the condition is still |
|
976 there and can only be discharged for special cases. |
|
977 In particular, the domain predicate guards the unfolding of our |
|
978 function, since it is there as a condition in the @{text psimp} |
|
979 rules. |
|
980 |
|
981 Now there is an important special case: We can actually get rid |
|
982 of the condition in the simplification rules, \emph{if the function |
|
983 is tail-recursive}. The reason is that for all tail-recursive |
|
984 equations there is a total function satisfying them, even if they |
|
985 are non-terminating. |
|
986 |
|
987 % A function is tail recursive, if each call to the function is either |
|
988 % equal |
|
989 % |
|
990 % So the outer form of the |
|
991 % |
|
992 %if it can be written in the following |
|
993 % form: |
|
994 % {term[display] "f x = (if COND x then BASE x else f (LOOP x))"} |
|
995 |
|
996 |
|
997 The function package internally does the right construction and can |
|
998 derive the unconditional simp rules, if we ask it to do so. Luckily, |
|
999 our @{const "findzero"} function is tail-recursive, so we can just go |
|
1000 back and add another option to the \cmd{function} command: |
|
1001 |
|
1002 \vspace{1ex} |
|
1003 \noindent\cmd{function} @{text "(domintros, tailrec) findzero :: \"(nat \<Rightarrow> nat) \<Rightarrow> nat \<Rightarrow> nat\""}\\% |
|
1004 \cmd{where}\isanewline% |
|
1005 \ \ \ldots\\% |
|
1006 |
|
1007 |
|
1008 \noindent Now, we actually get unconditional simplification rules, even |
|
1009 though the function is partial: |
|
1010 *} |
|
1011 |
|
1012 thm findzero.simps |
|
1013 |
|
1014 text {* |
|
1015 @{thm[display] findzero.simps} |
|
1016 |
|
1017 \noindent Of course these would make the simplifier loop, so we better remove |
|
1018 them from the simpset: |
|
1019 *} |
|
1020 |
|
1021 declare findzero.simps[simp del] |
|
1022 |
|
1023 text {* |
|
1024 Getting rid of the domain conditions in the simplification rules is |
|
1025 not only useful because it simplifies proofs. It is also required in |
|
1026 order to use Isabelle's code generator to generate ML code |
|
1027 from a function definition. |
|
1028 Since the code generator only works with equations, it cannot be |
|
1029 used with @{text "psimp"} rules. Thus, in order to generate code for |
|
1030 partial functions, they must be defined as a tail recursion. |
|
1031 Luckily, many functions have a relatively natural tail recursive |
|
1032 definition. |
|
1033 *} |
|
1034 |
|
1035 section {* Nested recursion *} |
962 section {* Nested recursion *} |
1036 |
963 |
1037 text {* |
964 text {* |
1038 Recursive calls which are nested in one another frequently cause |
965 Recursive calls which are nested in one another frequently cause |
1039 complications, since their termination proof can depend on a partial |
966 complications, since their termination proof can depend on a partial |