doc-src/TutorialI/Trie/document/Trie.tex
author nipkow
Wed, 11 Oct 2000 10:44:42 +0200
changeset 10187 0376cccd9118
parent 10171 59d6633835fa
child 10795 9e888d60d3e5
permissions -rw-r--r--
*** empty log message ***
     1 %
     2 \begin{isabellebody}%
     3 \def\isabellecontext{Trie}%
     4 %
     5 \begin{isamarkuptext}%
     6 To minimize running time, each node of a trie should contain an array that maps
     7 letters to subtries. We have chosen a (sometimes) more space efficient
     8 representation where the subtries are held in an association list, i.e.\ a
     9 list of (letter,trie) pairs.  Abstracting over the alphabet \isa{{\isacharprime}a} and the
    10 values \isa{{\isacharprime}v} we define a trie as follows:%
    11 \end{isamarkuptext}%
    12 \isacommand{datatype}\ {\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie\ {\isacharequal}\ Trie\ \ {\isachardoublequote}{\isacharprime}v\ option{\isachardoublequote}\ \ {\isachardoublequote}{\isacharparenleft}{\isacharprime}a\ {\isacharasterisk}\ {\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie{\isacharparenright}list{\isachardoublequote}%
    13 \begin{isamarkuptext}%
    14 \noindent
    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,
    17 which is fine because products are datatypes as well.
    18 We define two selector functions:%
    19 \end{isamarkuptext}%
    20 \isacommand{consts}\ value\ {\isacharcolon}{\isacharcolon}\ {\isachardoublequote}{\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie\ {\isasymRightarrow}\ {\isacharprime}v\ option{\isachardoublequote}\isanewline
    21 \ \ \ \ \ \ \ alist\ {\isacharcolon}{\isacharcolon}\ {\isachardoublequote}{\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie\ {\isasymRightarrow}\ {\isacharparenleft}{\isacharprime}a\ {\isacharasterisk}\ {\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie{\isacharparenright}list{\isachardoublequote}\isanewline
    22 \isacommand{primrec}\ {\isachardoublequote}value{\isacharparenleft}Trie\ ov\ al{\isacharparenright}\ {\isacharequal}\ ov{\isachardoublequote}\isanewline
    23 \isacommand{primrec}\ {\isachardoublequote}alist{\isacharparenleft}Trie\ ov\ al{\isacharparenright}\ {\isacharequal}\ al{\isachardoublequote}%
    24 \begin{isamarkuptext}%
    25 \noindent
    26 Association lists come with a generic lookup function:%
    27 \end{isamarkuptext}%
    28 \isacommand{consts}\ \ \ assoc\ {\isacharcolon}{\isacharcolon}\ {\isachardoublequote}{\isacharparenleft}{\isacharprime}key\ {\isacharasterisk}\ {\isacharprime}val{\isacharparenright}list\ {\isasymRightarrow}\ {\isacharprime}key\ {\isasymRightarrow}\ {\isacharprime}val\ option{\isachardoublequote}\isanewline
    29 \isacommand{primrec}\ {\isachardoublequote}assoc\ {\isacharbrackleft}{\isacharbrackright}\ x\ {\isacharequal}\ None{\isachardoublequote}\isanewline
    30 \ \ \ \ \ \ \ \ {\isachardoublequote}assoc\ {\isacharparenleft}p{\isacharhash}ps{\isacharparenright}\ x\ {\isacharequal}\isanewline
    31 \ \ \ \ \ \ \ \ \ \ \ {\isacharparenleft}let\ {\isacharparenleft}a{\isacharcomma}b{\isacharparenright}\ {\isacharequal}\ p\ in\ if\ a{\isacharequal}x\ then\ Some\ b\ else\ assoc\ ps\ x{\isacharparenright}{\isachardoublequote}%
    32 \begin{isamarkuptext}%
    33 Now we can define the lookup function for tries. It descends into the trie
    34 examining the letters of the search string one by one. As
    35 recursion on lists is simpler than on tries, let us express this as primitive
    36 recursion on the search string argument:%
    37 \end{isamarkuptext}%
    38 \isacommand{consts}\ \ \ lookup\ {\isacharcolon}{\isacharcolon}\ {\isachardoublequote}{\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie\ {\isasymRightarrow}\ {\isacharprime}a\ list\ {\isasymRightarrow}\ {\isacharprime}v\ option{\isachardoublequote}\isanewline
    39 \isacommand{primrec}\ {\isachardoublequote}lookup\ t\ {\isacharbrackleft}{\isacharbrackright}\ {\isacharequal}\ value\ t{\isachardoublequote}\isanewline
    40 \ \ \ \ \ \ \ \ {\isachardoublequote}lookup\ t\ {\isacharparenleft}a{\isacharhash}as{\isacharparenright}\ {\isacharequal}\ {\isacharparenleft}case\ assoc\ {\isacharparenleft}alist\ t{\isacharparenright}\ a\ of\isanewline
    41 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ None\ {\isasymRightarrow}\ None\isanewline
    42 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ {\isacharbar}\ Some\ at\ {\isasymRightarrow}\ lookup\ at\ as{\isacharparenright}{\isachardoublequote}%
    43 \begin{isamarkuptext}%
    44 As a first simple property we prove that looking up a string in the empty
    45 trie \isa{Trie\ None\ {\isacharbrackleft}{\isacharbrackright}} always returns \isa{None}. The proof merely
    46 distinguishes the two cases whether the search string is empty or not:%
    47 \end{isamarkuptext}%
    48 \isacommand{lemma}\ {\isacharbrackleft}simp{\isacharbrackright}{\isacharcolon}\ {\isachardoublequote}lookup\ {\isacharparenleft}Trie\ None\ {\isacharbrackleft}{\isacharbrackright}{\isacharparenright}\ as\ {\isacharequal}\ None{\isachardoublequote}\isanewline
    49 \isacommand{apply}{\isacharparenleft}case{\isacharunderscore}tac\ as{\isacharcomma}\ simp{\isacharunderscore}all{\isacharparenright}\isanewline
    50 \isacommand{done}%
    51 \begin{isamarkuptext}%
    52 Things begin to get interesting with the definition of an update function
    53 that adds a new (string,value) pair to a trie, overwriting the old value
    54 associated with that string:%
    55 \end{isamarkuptext}%
    56 \isacommand{consts}\ update\ {\isacharcolon}{\isacharcolon}\ {\isachardoublequote}{\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie\ {\isasymRightarrow}\ {\isacharprime}a\ list\ {\isasymRightarrow}\ {\isacharprime}v\ {\isasymRightarrow}\ {\isacharparenleft}{\isacharprime}a{\isacharcomma}{\isacharprime}v{\isacharparenright}trie{\isachardoublequote}\isanewline
    57 \isacommand{primrec}\isanewline
    58 \ \ {\isachardoublequote}update\ t\ {\isacharbrackleft}{\isacharbrackright}\ \ \ \ \ v\ {\isacharequal}\ Trie\ {\isacharparenleft}Some\ v{\isacharparenright}\ {\isacharparenleft}alist\ t{\isacharparenright}{\isachardoublequote}\isanewline
    59 \ \ {\isachardoublequote}update\ t\ {\isacharparenleft}a{\isacharhash}as{\isacharparenright}\ v\ {\isacharequal}\isanewline
    60 \ \ \ \ \ {\isacharparenleft}let\ tt\ {\isacharequal}\ {\isacharparenleft}case\ assoc\ {\isacharparenleft}alist\ t{\isacharparenright}\ a\ of\isanewline
    61 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ None\ {\isasymRightarrow}\ Trie\ None\ {\isacharbrackleft}{\isacharbrackright}\ {\isacharbar}\ Some\ at\ {\isasymRightarrow}\ at{\isacharparenright}\isanewline
    62 \ \ \ \ \ \ in\ Trie\ {\isacharparenleft}value\ t{\isacharparenright}\ {\isacharparenleft}{\isacharparenleft}a{\isacharcomma}update\ tt\ as\ v{\isacharparenright}{\isacharhash}alist\ t{\isacharparenright}{\isacharparenright}{\isachardoublequote}%
    63 \begin{isamarkuptext}%
    64 \noindent
    65 The base case is obvious. In the recursive case the subtrie
    66 \isa{tt} associated with the first letter \isa{a} is extracted,
    67 recursively updated, and then placed in front of the association list.
    68 The old subtrie associated with \isa{a} is still in the association list
    69 but no longer accessible via \isa{assoc}. Clearly, there is room here for
    70 optimizations!
    71 
    72 Before we start on any proofs about \isa{update} we tell the simplifier to
    73 expand all \isa{let}s and to split all \isa{case}-constructs over
    74 options:%
    75 \end{isamarkuptext}%
    76 \isacommand{declare}\ Let{\isacharunderscore}def{\isacharbrackleft}simp{\isacharbrackright}\ option{\isachardot}split{\isacharbrackleft}split{\isacharbrackright}%
    77 \begin{isamarkuptext}%
    78 \noindent
    79 The reason becomes clear when looking (probably after a failed proof
    80 attempt) at the body of \isa{update}: it contains both
    81 \isa{let} and a case distinction over type \isa{option}.
    82 
    83 Our main goal is to prove the correct interaction of \isa{update} and
    84 \isa{lookup}:%
    85 \end{isamarkuptext}%
    86 \isacommand{theorem}\ {\isachardoublequote}{\isasymforall}t\ v\ bs{\isachardot}\ lookup\ {\isacharparenleft}update\ t\ as\ v{\isacharparenright}\ bs\ {\isacharequal}\isanewline
    87 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ {\isacharparenleft}if\ as{\isacharequal}bs\ then\ Some\ v\ else\ lookup\ t\ bs{\isacharparenright}{\isachardoublequote}%
    88 \begin{isamarkuptxt}%
    89 \noindent
    90 Our plan is to induct on \isa{as}; hence the remaining variables are
    91 quantified. From the definitions it is clear that induction on either
    92 \isa{as} or \isa{bs} is required. The choice of \isa{as} is merely
    93 guided by the intuition that simplification of \isa{lookup} might be easier
    94 if \isa{update} has already been simplified, which can only happen if
    95 \isa{as} is instantiated.
    96 The start of the proof is completely conventional:%
    97 \end{isamarkuptxt}%
    98 \isacommand{apply}{\isacharparenleft}induct{\isacharunderscore}tac\ as{\isacharcomma}\ auto{\isacharparenright}%
    99 \begin{isamarkuptxt}%
   100 \noindent
   101 Unfortunately, this time we are left with three intimidating looking subgoals:
   102 \begin{isabelle}
   103 ~1.~\dots~{\isasymLongrightarrow}~lookup~\dots~bs~=~lookup~t~bs\isanewline
   104 ~2.~\dots~{\isasymLongrightarrow}~lookup~\dots~bs~=~lookup~t~bs\isanewline
   105 ~3.~\dots~{\isasymLongrightarrow}~lookup~\dots~bs~=~lookup~t~bs
   106 \end{isabelle}
   107 Clearly, if we want to make headway we have to instantiate \isa{bs} as
   108 well now. It turns out that instead of induction, case distinction
   109 suffices:%
   110 \end{isamarkuptxt}%
   111 \isacommand{apply}{\isacharparenleft}case{\isacharunderscore}tac{\isacharbrackleft}{\isacharbang}{\isacharbrackright}\ bs{\isacharcomma}\ auto{\isacharparenright}\isanewline
   112 \isacommand{done}%
   113 \begin{isamarkuptext}%
   114 \noindent
   115 All methods ending in \isa{tac} take an optional first argument that
   116 specifies the range of subgoals they are applied to, where \isa{{\isacharbrackleft}{\isacharbang}{\isacharbrackright}} means
   117 all subgoals, i.e.\ \isa{{\isacharbrackleft}{\isadigit{1}}{\isacharminus}{\isadigit{3}}{\isacharbrackright}} in our case. Individual subgoal numbers,
   118 e.g. \isa{{\isacharbrackleft}{\isadigit{2}}{\isacharbrackright}} are also allowed.
   119 
   120 This proof may look surprisingly straightforward. However, note that this
   121 comes at a cost: the proof script is unreadable because the intermediate
   122 proof states are invisible, and we rely on the (possibly brittle) magic of
   123 \isa{auto} (\isa{simp{\isacharunderscore}all} will not do---try it) to split the subgoals
   124 of the induction up in such a way that case distinction on \isa{bs} makes
   125 sense and solves the proof. Part~\ref{Isar} shows you how to write readable
   126 and stable proofs.%
   127 \end{isamarkuptext}%
   128 \end{isabellebody}%
   129 %%% Local Variables:
   130 %%% mode: latex
   131 %%% TeX-master: "root"
   132 %%% End: