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