doc-src/TutorialI/Trie/Trie.thy
changeset 9458 c613cd06d5cf
parent 8771 026f37a86ea7
child 9541 d17c0b34d5c8
equal deleted inserted replaced
9457:966974a7a5b3 9458:c613cd06d5cf
     5 To minimize running time, each node of a trie should contain an array that maps
     5 To minimize running time, each node of a trie should contain an array that maps
     6 letters to subtries. We have chosen a (sometimes) more space efficient
     6 letters to subtries. We have chosen a (sometimes) more space efficient
     7 representation where the subtries are held in an association list, i.e.\ a
     7 representation where the subtries are held in an association list, i.e.\ a
     8 list of (letter,trie) pairs.  Abstracting over the alphabet \isa{'a} and the
     8 list of (letter,trie) pairs.  Abstracting over the alphabet \isa{'a} and the
     9 values \isa{'v} we define a trie as follows:
     9 values \isa{'v} we define a trie as follows:
    10 *}
    10 *};
    11 
    11 
    12 datatype ('a,'v)trie = Trie  "'v option"  "('a * ('a,'v)trie)list";
    12 datatype ('a,'v)trie = Trie  "'v option"  "('a * ('a,'v)trie)list";
    13 
    13 
    14 text{*\noindent
    14 text{*\noindent
    15 The first component is the optional value, the second component the
    15 The first component is the optional value, the second component the
    16 association list of subtries.  This is an example of nested recursion involving products,
    16 association list of subtries.  This is an example of nested recursion involving products,
    17 which is fine because products are datatypes as well.
    17 which is fine because products are datatypes as well.
    18 We define two selector functions:
    18 We define two selector functions:
    19 *}
    19 *};
    20 
    20 
    21 consts value :: "('a,'v)trie \\<Rightarrow> 'v option"
    21 consts value :: "('a,'v)trie \\<Rightarrow> 'v option"
    22        alist :: "('a,'v)trie \\<Rightarrow> ('a * ('a,'v)trie)list";
    22        alist :: "('a,'v)trie \\<Rightarrow> ('a * ('a,'v)trie)list";
    23 primrec "value(Trie ov al) = ov";
    23 primrec "value(Trie ov al) = ov";
    24 primrec "alist(Trie ov al) = al";
    24 primrec "alist(Trie ov al) = al";
    25 
    25 
    26 text{*\noindent
    26 text{*\noindent
    27 Association lists come with a generic lookup function:
    27 Association lists come with a generic lookup function:
    28 *}
    28 *};
    29 
    29 
    30 consts   assoc :: "('key * 'val)list \\<Rightarrow> 'key \\<Rightarrow> 'val option";
    30 consts   assoc :: "('key * 'val)list \\<Rightarrow> 'key \\<Rightarrow> 'val option";
    31 primrec "assoc [] x = None"
    31 primrec "assoc [] x = None"
    32         "assoc (p#ps) x =
    32         "assoc (p#ps) x =
    33            (let (a,b) = p in if a=x then Some b else assoc ps x)";
    33            (let (a,b) = p in if a=x then Some b else assoc ps x)";
    35 text{*
    35 text{*
    36 Now we can define the lookup function for tries. It descends into the trie
    36 Now we can define the lookup function for tries. It descends into the trie
    37 examining the letters of the search string one by one. As
    37 examining the letters of the search string one by one. As
    38 recursion on lists is simpler than on tries, let us express this as primitive
    38 recursion on lists is simpler than on tries, let us express this as primitive
    39 recursion on the search string argument:
    39 recursion on the search string argument:
    40 *}
    40 *};
    41 
    41 
    42 consts   lookup :: "('a,'v)trie \\<Rightarrow> 'a list \\<Rightarrow> 'v option";
    42 consts   lookup :: "('a,'v)trie \\<Rightarrow> 'a list \\<Rightarrow> 'v option";
    43 primrec "lookup t [] = value t"
    43 primrec "lookup t [] = value t"
    44         "lookup t (a#as) = (case assoc (alist t) a of
    44         "lookup t (a#as) = (case assoc (alist t) a of
    45                               None \\<Rightarrow> None
    45                               None \\<Rightarrow> None
    47 
    47 
    48 text{*
    48 text{*
    49 As a first simple property we prove that looking up a string in the empty
    49 As a first simple property we prove that looking up a string in the empty
    50 trie \isa{Trie~None~[]} always returns \isa{None}. The proof merely
    50 trie \isa{Trie~None~[]} always returns \isa{None}. The proof merely
    51 distinguishes the two cases whether the search string is empty or not:
    51 distinguishes the two cases whether the search string is empty or not:
    52 *}
    52 *};
    53 
    53 
    54 lemma [simp]: "lookup (Trie None []) as = None";
    54 lemma [simp]: "lookup (Trie None []) as = None";
    55 apply(case_tac as, auto).;
    55 by(case_tac as, auto);
    56 
    56 
    57 text{*
    57 text{*
    58 Things begin to get interesting with the definition of an update function
    58 Things begin to get interesting with the definition of an update function
    59 that adds a new (string,value) pair to a trie, overwriting the old value
    59 that adds a new (string,value) pair to a trie, overwriting the old value
    60 associated with that string:
    60 associated with that string:
    61 *}
    61 *};
    62 
    62 
    63 consts update :: "('a,'v)trie \\<Rightarrow> 'a list \\<Rightarrow> 'v \\<Rightarrow> ('a,'v)trie";
    63 consts update :: "('a,'v)trie \\<Rightarrow> 'a list \\<Rightarrow> 'v \\<Rightarrow> ('a,'v)trie";
    64 primrec
    64 primrec
    65   "update t []     v = Trie (Some v) (alist t)"
    65   "update t []     v = Trie (Some v) (alist t)"
    66   "update t (a#as) v =
    66   "update t (a#as) v =
    77 optimizations!
    77 optimizations!
    78 
    78 
    79 Before we start on any proofs about \isa{update} we tell the simplifier to
    79 Before we start on any proofs about \isa{update} we tell the simplifier to
    80 expand all \isa{let}s and to split all \isa{case}-constructs over
    80 expand all \isa{let}s and to split all \isa{case}-constructs over
    81 options:
    81 options:
    82 *}
    82 *};
    83 
    83 
    84 theorems [simp] = Let_def;
    84 theorems [simp] = Let_def;
    85 theorems [split] = option.split;
    85 theorems [split] = option.split;
    86 
    86 
    87 text{*\noindent
    87 text{*\noindent
    89 attempt) at the body of \isa{update}: it contains both
    89 attempt) at the body of \isa{update}: it contains both
    90 \isa{let} and a case distinction over type \isa{option}.
    90 \isa{let} and a case distinction over type \isa{option}.
    91 
    91 
    92 Our main goal is to prove the correct interaction of \isa{update} and
    92 Our main goal is to prove the correct interaction of \isa{update} and
    93 \isa{lookup}:
    93 \isa{lookup}:
    94 *}
    94 *};
    95 
    95 
    96 theorem "\\<forall>t v bs. lookup (update t as v) bs =
    96 theorem "\\<forall>t v bs. lookup (update t as v) bs =
    97                     (if as=bs then Some v else lookup t bs)";
    97                     (if as=bs then Some v else lookup t bs)";
    98 
    98 
    99 txt{*\noindent
    99 txt{*\noindent
   102 \isa{as} or \isa{bs} is required. The choice of \isa{as} is merely
   102 \isa{as} or \isa{bs} is required. The choice of \isa{as} is merely
   103 guided by the intuition that simplification of \isa{lookup} might be easier
   103 guided by the intuition that simplification of \isa{lookup} might be easier
   104 if \isa{update} has already been simplified, which can only happen if
   104 if \isa{update} has already been simplified, which can only happen if
   105 \isa{as} is instantiated.
   105 \isa{as} is instantiated.
   106 The start of the proof is completely conventional:
   106 The start of the proof is completely conventional:
   107 *}
   107 *};
   108 
   108 
   109 apply(induct_tac as, auto);
   109 apply(induct_tac as, auto);
   110 
   110 
   111 txt{*\noindent
   111 txt{*\noindent
   112 Unfortunately, this time we are left with three intimidating looking subgoals:
   112 Unfortunately, this time we are left with three intimidating looking subgoals:
   116 ~3.~\dots~{\isasymLongrightarrow}~lookup~\dots~bs~=~lookup~t~bs%
   116 ~3.~\dots~{\isasymLongrightarrow}~lookup~\dots~bs~=~lookup~t~bs%
   117 \end{isabellepar}%
   117 \end{isabellepar}%
   118 Clearly, if we want to make headway we have to instantiate \isa{bs} as
   118 Clearly, if we want to make headway we have to instantiate \isa{bs} as
   119 well now. It turns out that instead of induction, case distinction
   119 well now. It turns out that instead of induction, case distinction
   120 suffices:
   120 suffices:
   121 *}
   121 *};
   122 
   122 
   123 apply(case_tac[!] bs);
   123 apply(case_tac[!] bs);
   124 apply(auto).;
   124 by(auto);
   125 
   125 
   126 text{*\noindent
   126 text{*\noindent
   127 Both \isaindex{case_tac} and \isaindex{induct_tac}
   127 Both \isaindex{case_tac} and \isaindex{induct_tac}
   128 take an optional first argument that specifies the range of subgoals they are
   128 take an optional first argument that specifies the range of subgoals they are
   129 applied to, where \isa{!} means all subgoals, i.e.\ \isa{[1-3]} in our case. Individual
   129 applied to, where \isa{!} means all subgoals, i.e.\ \isa{[1-3]} in our case. Individual
   134 intermediate proof states are invisible, and we rely on the (possibly
   134 intermediate proof states are invisible, and we rely on the (possibly
   135 brittle) magic of \isa{auto} (after the induction) to split the remaining
   135 brittle) magic of \isa{auto} (after the induction) to split the remaining
   136 goals up in such a way that case distinction on \isa{bs} makes sense and
   136 goals up in such a way that case distinction on \isa{bs} makes sense and
   137 solves the proof. Part~\ref{Isar} shows you how to write readable and stable
   137 solves the proof. Part~\ref{Isar} shows you how to write readable and stable
   138 proofs.
   138 proofs.
   139 *}
   139 *};
   140 
   140 
   141 (*<*)
   141 (*<*)
   142 end
   142 end;
   143 (*>*)
   143 (*>*)