1 theory Cas13_vezbe_resenja
   2   imports "HOL-Library.Mapping" "HOL-Library.RBT_Mapping"
   3 
   4 begin
   5 
   6 sectionRačunanje vrednosti prefiksno zadatog izraza i generisanje koda za stek
   7 mašinu
   8 
   9 textNa sličan način kao što smo definisali tip binarnog drveta, možemo
  10 definisati tip prefiksnog izraza sa unapred definisanim tipom podataka i
  11 dozvoljenim operacijama.
  12 
  13 subsectionPrefiksni izraz nad konstantama
  14 
  15 text\paragraph{Prefiksni izraz:} Prefiksno zadat izraz nad konstantama tipa
  16 @{typ nat}, sa operacijama sabiranja, oduzimanja i množenja (koje ćemo
  17 označiti PlusI, MinusI, PutaI) se može definisati na sledeći način. 
  18 
  19 datatype 
  20   Izraz = Const nat | PlusI Izraz Izraz | MinusI Izraz Izraz | PutaI Izraz Izraz
  21 
  22 term PlusI  operator
  23 term "PlusI (Const 3) (Const 5)"  primer prefiksno zadatog izraza
  24 
  25 textOva definicija veoma podseća na implementaciju koja bi mogla da bude
  26 korišćena u jeziku Haskell tako da ćemo parsiranje ovakvog izraza implementirati
  27 na sličan način kao što bi se parsirao aritmetički izraz u Haskell-u.
  28 
  29 text\paragraph{Vrednost izraza:} Sada lako možemo definisati funkciju koja
  30 računa vrednost ovako zadatog izraza. Vrednost izraza koji se sastoji samo od
  31 konstante je upravo ta konstanta, u suprotnom se vrednost izraza računa prema
  32 operatoru koji ga gradi.
  33 
  34 Napomena: kako radimo sa prirodnim brojevima, vrednost oduzimanja će uvek biti
  35 broj koji je veći ili jednak od 0.
  36 
  37 primrec vrednost :: "Izraz ⇒ nat" where
  38 "vrednost (Const x) = x" |
  39 "vrednost (PlusI i1 i2) = vrednost i1 + vrednost i2" |
  40 "vrednost (PutaI i1 i2) = vrednost i1 * vrednost i2" |
  41 "vrednost (MinusI i1 i2) = vrednost i1 - vrednost i2"
  42 
  43 value "vrednost (PlusI (Const 3) (Const 5))"  rezultat je 8, primetimo da moramo koristiti 
  44 zagrade oko ovako zapisanog izraza
  45 value "vrednost (MinusI (Const 3) (Const 5))"  rezultat je 0
  46 
  47 textUz ključnu reč \verb!definition! možemo da uvedemo nekoliko definicija:
  48 
  49 definition x1 :: "Izraz" where 
  50 "x1 = PlusI (Const 3) (Const 5)"
  51 value "vrednost x1"  rezultat je 8
  52 
  53 definition x2 :: "Izraz" where 
  54 "x2 = PutaI (Const 3)(MinusI (Const 5) (Const 2))"
  55 value "vrednost x2"  rezultat je 9
  56 
  57 definition x3 :: "Izraz" where 
  58 "x3 = PutaI (PlusI (Const 3) (Const 4)) (MinusI (Const 5)(Const 2))"
  59 value "vrednost x3"  rezultat je 21
  60 
  61 textNa ovaj način smo prikazali kako možemo direktno da izračunamo vrednost
  62 prefiksno zadatog izraza. Sada ćemo pokazati drugi način za računanje ove
  63 vrednosti. Želimo da izgenerišemo program koji bi izračunavao vrednost ovako
  64 zadatog prefiksnog izraza. Prvi korak je definisanje skupa dozvoljenih
  65 operacija.
  66 
  67 \paragraph{Dozvoljene operacije: } Pored operacija nad prirodnim brojevima
  68 (množenje, sabiranje i oduzimanje), potrebno je i da definišemo operaciju
  69 smeštanja elementa na stek. Kako radimo sa prirodnim brojevima, stek možemo
  70 predstaviti uz pomoć liste prirodnih brojeva. Uvešćemo ga uz pomoć ključne reči
  71 \verb!type_synonym!. Na ovaj način dobijamo samo skraćeni zapis za listu
  72 prirodnih brojeva i povećavamo čitljivost našeg koda. Interno, ovakve skraćenice
  73 se u potpunosti raspisuju.
  74 
  75 datatype Operacija = OpPuta | OpPlus | OpMinus | OpPush nat
  76 
  77 type_synonym Stek = "nat list"
  78 
  79 text\paragraph{Primena operacija:} Sve operacije se izvršavaju nad stekom na
  80 uobičajeni način. Operacija smeštanja elementa na stek u stvari dodaje element
  81 na početak steka (odnosno na početak liste) pa možemo korisiti operaciju
  82 dopisivanja elementa na početak liste (#). Ostale operacije se izvršavaju nad
  83 dva elementa koja se nalaze na vrhu steka tako što se skidaju ta dva elementa sa
  84 steka, izvršava se data operacija, i na kraju se rezultat vraća na stek.
  85 
  86 fun izvrsiOp :: "Operacija ⇒ Stek ⇒ Stek" where
  87 "izvrsiOp (OpPush x) xs = x # xs" |
  88 "izvrsiOp OpPuta (x # y # xs) = (x * y) # xs" |
  89 "izvrsiOp OpPlus (x # y # xs) = (x + y) # xs" |
  90 "izvrsiOp OpMinus (x # y # xs) = (x - y) # xs" 
  91 
  92 text\paragraph{Prevođenje izraza i generisanje koda za stek mašinu:} Sada
  93 možemo definisati funkciju koja prevodi dati izraz u listu operacija. Upravo
  94 tako ćemo definisati program - lista operacija nad tipom nat. Ovako dobijena
  95 lista operacija se čita zdesna na levo.
  96 
  97 Prevođenje je manje više trivijalno, konstantni izraz se prevodi u operaciju
  98 dodavanja elementa na stek, dok se izraz koji počinje operatorom prevodi u
  99 operaciju nad stekom (koja odgovara tom konkretnom operatoru) nakon čega
 100 rekurzivno prevodimo njegov prvi pa zatim drugi operand.
 101 
 102 type_synonym Program = "Operacija list"
 103 
 104 primrec prevedi :: "Izraz ⇒ Program" where 
 105 "prevedi (Const x) = [OpPush x]" |
 106 "prevedi (PlusI i1 i2) = [OpPlus] @ prevedi i1 @ prevedi i2" | 
 107 "prevedi (MinusI i1 i2) = [OpMinus] @ prevedi i1 @ prevedi i2" |
 108 "prevedi (PutaI i1 i2) = [OpPuta] @ prevedi i1 @ prevedi i2"
 109 
 110 term prevedi
 111 
 112 textDa bismo izgenerisali kod za stek mašinu za dati izraz (x1 = PlusI (Const
 113 3) (Const 5)), prvo ga prevodimo u listu operacija i dobijamo [OpPlus, OpPush
 114 3, OpPush 5]. Ovako dobijen program čitamo zdesna na levo, što znači da prvo na
 115 stek stavljamo broj 5, nakon čega stavljamo broj 3 pa tek onda operator
 116 sabiranja.
 117 
 118 value x1
 119 value "prevedi x1"
 120 
 121 textU slučaju malo komplikovanijeg izraza x2, dobijamo naredni program.
 122 Odnosno, prvo na stek stavljamo broj 2, pa broj 5, pa operaciju oduzimanja;
 123 nakon toga stavljamo broj 3 pa operaciju množenja.
 124 
 125 value x2  PutaI (Const 3) (MinusI (Const 5) (Const 2))
 126 value "prevedi x2"  [OpPuta, OpPush 3, OpMinus, OpPush 5, OpPush 2]
 127 
 128 text\paragraph{Izvršavanje programa:} Sada je potrebno definisati rekurzivnu
 129 funkciju koja će izvršiti ovako dobijeni program. Da bi se program izvršio
 130 potrebno je da funkcija zna na kom steku ćemo čuvati podatke (na početku je taj
 131 stek prazan). Povratna vrednost ovakve funkcije će se naći na vrhu rezultujućeg
 132 steka ali to ne možemo tako odmah da zapišemo (zapisaćemo kasnije u okviru
 133 leme), pa kao povratni tip očekujemo isto stek.
 134 
 135 Ako nemamo više operacija i naš program je prazan, vraćamo tekuće stanje steka.
 136 Inače pozivamo funkciju \verb!izvrsiOp! da izvrši operaciju koja se nalazi na
 137 početku našeg programa. Tu operaciju ćemo izvršiti nad stekom koji se dobija
 138 rekurzivnim pozivom nad repom liste (tj. rekurzivnim pozivom nad ostatkom
 139 programa). 
 140 
 141 primrec izvrsiProgram :: "Program ⇒ Stek ⇒ Stek" where
 142 "izvrsiProgram [] s = s" |
 143 "izvrsiProgram (op # p) s = izvrsiOp op (izvrsiProgram p s)"
 144 
 145 textRezultat ovakve funkcije će biti stek koji će imati samo jedan element, i
 146 taj element će upravo biti jednak vrednosti prefiksno zadatog izraza.
 147 
 148 value x1 
 149 value "vrednost x1"  rezultat 8
 150 value "prevedi x1"  rezultat [OpPlus, OpPush 3, OpPush 5]
 151 value "izvrsiProgram (prevedi x1) []"  rezultat [8]
 152 
 153 textKako je na početku stek prazan, napravićemo školjku oko ovako definisane
 154 funkcije i nazvaćemo je \verb!racunar!. Sada ćemo dokazati da je ovakva
 155 definicija dobra, odnosno da važi teorema hd_racunar_vrednost: "hd (racunar i)
 156 = vrednost i".
 157 
 158 definition racunar where 
 159 "racunar i = izvrsiProgram (prevedi i) []"
 160 
 161 value "racunar x1"  rezultat [8]
 162 
 163 textU dokazu leme hd_racunar_vrednost, nakon raspisivanja ove definicije
 164 dobijamo izraz oblika hd (izvrsiProgram (prevedi i) []) = vrednost i. Odnosno
 165 imamo poziv funkcije izvrsiProgram na početku izvršavanja, kada je stek
 166 prazan. Prvo ćemo pokušati sa primenom indukcije (prema definiciji tipa izraz)
 167 i automatskih alata, i vidimo da se sistem zaustavio kod primene funkcije
 168 izvrsiOp koja menja stanje steka. Kako je u ovoj lemi početno stanje steka
 169 fiksirano na prazan stek, primećujemo da moramo razmotriti šta se dešava kada
 170 stek nije prazan, odnosno kako neko početno stanje steka utiče na vrednost
 171 izraza koji nam je preostao da izračunamo.
 172 
 173 Pošto je izraz koji pokušavamo da dokažemo hd (izvrsiProgram (prevedi i) []) =
 174 vrednost i u stvari ekvivalentan izrazu izvrsiProgram (prevedi i) [] =
 175 [vrednost i], uopštenjem ovog izraza na proizvoljan stek dobijamo lemu
 176 izvrsiProgram_prevedi: izvrsiProgram (prevedi i) s = vrednost i # s.
 177 Odnosno, izvršavanje preostalog dela programa ne utiče na prethodno stanje steka
 178 (odnosno na podatke koji se već nalaze na steku), već se rezultat dopisuje na
 179 početak steka.
 180 
 181 U dokazu ove leme ponovo pokušavamo sa indukcijom i automatskim alatima, ali
 182 opet dolazimo u situaciju da pozivamo funkciju koja treba da menja stanje steka.
 183 Kao što smo ranije videli, ovakva situacija se rešava ključnom rečju
 184 arbitrary. Sada ponovo pokušavamo sa automatskim dokazivačem i vidimo da je
 185 sistem stao kod izraza oblika (izvrsiProgram (prevedi i1 @ prevedi i2) s pa
 186 formulišemo još jednu dodatnu lemu izvrsiProgram_append. Leva strana leme se
 187 formuliše prema stanju u kom je sistem stao, i sami treba da zaključimo kako
 188 izgleda desna strana. Naime pošto smo rekli da se program izvršava tako što se
 189 dobijena lista primenjuje zdesna na levo, očekujemo da ova lema tvrdi da se
 190 program koji se dobija nadovezivanjem dva programa p1 i p2 izvršava tako što
 191 se prvo primeni program p2 (na originalni stek) pa nakon toga program p1 (na
 192 stek dobijen primenom programa p2). Ova lema se automatski dokazuje uz pomoć
 193 indukcije.
 194 
 195 Nakon dodavanja ovih dveju dodatnih lema i njihovog ubacivanja u simplifikator
 196 (ključnom rečju simp), originalna lema se dokazuje automatski samo uz pomoć
 197 simplifikatora.
 198 
 199  5.3
 200 lemma izvrsiProgram_append[simp]: 
 201   shows "izvrsiProgram (p1 @ p2) s = izvrsiProgram p1 (izvrsiProgram p2 s)"
 202   apply (induction p1)
 203    apply auto
 204   done
 205 
 206  5.2
 207 lemma izvrsiProgram_prevedi [simp]: 
 208   "izvrsiProgram (prevedi i) s = vrednost i # s"
 209  apply (induction i)
 210  apply auto
 211   apply (induction i arbitrary: s)
 212      apply auto
 213   done
 214 
 215  5.1 krećemo odavde
 216 theorem hd_racunar_vrednost:
 217   shows "hd (racunar i) = vrednost i"
 218   unfolding racunar_def
 219  apply (induction i)
 220  apply auto
 221   by simp
 222 
 223 
 224 subsectionPrefiksni izraz sa promenljivima
 225 
 226 textSada želimo da definišemo prefiksni izraz u kome se mogu javljati i
 227 promenljive. Znači pored konstanti tipa možemo imati i promenjive tipa @{typ
 228 nat}, ali da bismo naglasili da je to tip koji odgovara promenjivi uvešćemo ga
 229 preko sinonima.
 230 
 231 Napomena: da ne bismo imali konflikt sa prethodnim definicijama koristićemo nova
 232 imena za tipove i operatore.
 233 
 234 type_synonym var_num = nat
 235 datatype exp = Var var_num | Con int | Add exp exp | Mult exp exp | Minus exp exp
 236 
 237 textSada je potrebno zadati vrednosti promeljivima koje se javljaju u izrazu.
 238 Prvi način je zadavanjem funkcije koja će te promenljive slikati u cele brojeve.
 239 Zbog jednostavnosti, pretpostavimo da se u izrazu javljaju samo naredne
 240 promenljive: x0 = 1; x1 = 2; x2 = 3. Njihove vrednosti ćemo zadati preko naredne
 241 definicije.
 242 
 243 definition val1 :: "var_num ⇒ int" where
 244   "val1 x = (if x = 0 then 1 else if x = 1 then 2 else 3)"
 245 
 246 textSada ćemo definisati funkciju koja izračunava vrednost ovako zadatog
 247 izraza. U odnosu na inicijalni primer prefiksnog izraza u kome smo imali samo
 248 konstante, ova funkcija mora imati dodatni parametar - funkciju f koja slika
 249 promenljive u njihove stvarne vrednost.
 250 
 251 primrec value_exp :: "exp ⇒ (var_num ⇒ int) ⇒ int" where 
 252 "value_exp (Var x) f = f x" |
 253 "value_exp (Con y) f = y" |
 254 "value_exp (Add exp1 exp2) f = (value_exp exp1 f) + (value_exp exp2 f)" |
 255 "value_exp (Mult exp1 exp2) f = (value_exp exp1 f) * (value_exp exp2 f)" |
 256 "value_exp (Minus exp1 exp2) f = (value_exp exp1 f) - (value_exp exp2 f)" 
 257 
 258 textOvako definisana funkcija će se pozivati na naredni način. Koristićemo
 259 funkciju val1 koju smo već definisali.
 260 
 261 value "value_exp (Add (Con 2) (Var 1)) val1"  vrednost 4
 262 definition x4 :: "exp" where "x4 = Add (Con 2) (Var 0)"
 263 value "value_exp x4 val1"  vrednost 3
 264 
 265 textNaredni korak je definisanje dozvoljenih operacija. Kako sada radimo i sa
 266 promenljivima, biće potrebno dodati posebnu operaciju koja čita vrednost te
 267 promenljive. Stek se definiše na isti način kao ranije, lista (u ovom slučaju)
 268 celih brojeva.
 269 
 270 datatype Operation = OpAdd | OpMult | OpMin | OpPush int | OpRead var_num 
 271 
 272 type_synonym Stack = "int list"
 273 
 274 textI prilikom implementacije funkcije koja izvršava jednu operaciju moramo
 275 imati dodatni argument, funkciju koja slika promenljive u celobrojne vrednosti
 276 (za slučaj kada naiđemo na promenljivu).
 277 
 278 fun executeOp :: "Operation ⇒ Stack ⇒ (var_num ⇒ int) ⇒ Stack" where
 279 "executeOp OpMult (x # y # xs) f = (x * y) # xs" |
 280 "executeOp OpAdd (x # y # xs) f = (x + y) # xs" |
 281 "executeOp OpMin (x # y # xs) f = (x - y) # xs" |
 282 "executeOp (OpPush x) xs f = (x # xs)" |
 283 "executeOp (OpRead v) xs f = (f v # xs)"
 284 
 285 textProgram i prevođenje izraza u program se izvršava slično kao ranije.
 286 
 287 type_synonym Prog = "Operation list"
 288 
 289 primrec translate :: "exp ⇒ Prog" where
 290 "translate (Var x)  = [OpRead x]" |
 291 "translate (Con c) = [OpPush c]" |
 292 "translate (Add e1 e2) = [OpAdd] @ translate e1 @ translate e2" |
 293 "translate (Minus e1 e2) = [OpMin] @ translate e1 @ translate e2" |
 294 "translate (Mult e1 e2) = [OpMult] @ translate e1 @ translate e2" 
 295 
 296 textFunkcija koja izvršava program takođe ima kao dodatni argument funkciju
 297 koja slika promenljive u celobrojne vrednosti.
 298 
 299 primrec executeProg :: "Prog ⇒ Stack ⇒ (var_num ⇒ int) ⇒ Stack" where
 300 "executeProg [] s f = s" |
 301 "executeProg (op # p) s f = executeOp op (executeProg p s f) f"
 302 
 303 textPoziv ovako definisanih funkcija:
 304 
 305 value "x4"  Add (Con 2) (Var 0)
 306 value "value_exp x4 val1"  vrednost 3
 307 value "translate x4"  [OpAdd, Operation.OpPush 2, OpRead 0]
 308 value "executeProg (translate x4) [] val1"  rezultat [3]
 309 
 310 textSada ćemo uvesti narednu definiciju i dokazati (na sličan način kao u
 311 prethodnom primeru) da je ta definicija dobro definisana.
 312 
 313 definition computer where 
 314 "computer i f = executeProg (translate i) [] f"
 315 
 316 lemma executeProg_append[simp]: 
 317   shows "executeProg (p1 @ p2) s f = executeProg p1 (executeProg p2 s f) f"
 318   apply (induction p1)
 319    apply auto
 320 done
 321 
 322 lemma executeProg_translate[simp]: 
 323   shows "executeProg (translate i) s f = (value_exp i f) # s"
 324   apply (induction i arbitrary: s)
 325       apply auto
 326 done
 327 
 328 theorem "hd(computer i f) = value_exp i f"
 329   unfolding computer_def
 330 by simp
 331 
 332 
 333 subsection Interpretator koji izračunava vrednost izraza
 334 
 335 textPrethodno rešenje sa korišćenjem funkcije se može uopštiti. Sada ćemo
 336 prikazati kako možemo vrednosti promenljivih da čuvamo u mapi koja će biti
 337 korišćena da imena promenljivih preslika u njihove stvarne vrednosti.
 338 
 339 type_synonym var_num' = nat
 340 datatype exp' = Var var_num' | Const int | Add exp' exp' | Mult exp' exp' | Minus exp' exp'
 341 
 342 
 343  Čitanje iz memorije ćemo implementirati preko tabele koja će biti
 344 predstavljena uz pomoć mapa. Uključujemo teoriju HOL-Library.Mapping.
 345 
 346 @{url https://isabelle.in.tum.de/library/HOL/HOL-Library/Mapping.html}
 347 
 348 textMapa je predstavljena kao skup uređenih parova, što zapisujemo na sledeći
 349 način:
 350 
 351 type_synonym Memory = "(nat, int) mapping"
 352 
 353 textZa čitanje iz mape koristićemo funkciju lookup_default koja je
 354 implementirana tako da vraća svoj prvi argument (podrazumevanu, default
 355 vrednost) ako u mapi ne postoji uređeni par koji odgovara vrednosti koja se
 356 traži.
 357 
 358 definition read_memory where
 359   "read_memory m x = Mapping.lookup_default 0 m x"
 360 
 361 textFunkcija koja izračunava vrednost izraza tako što čita vrednosti
 362 promenljivih iz memorije se može implementirati na sledeći način:
 363 
 364 primrec value_exp_map:: "exp' ⇒ Memory ⇒ int" where
 365 "value_exp_map (Var x) m = read_memory m x" |
 366 "value_exp_map (Const y) m = y" |
 367 "value_exp_map (Add exp1 exp2) m = (value_exp_map exp1 m) + (value_exp_map exp2 m)" |
 368 "value_exp_map (Mult exp1 exp2) m = (value_exp_map exp1 m) * (value_exp_map exp2 m)" |
 369 "value_exp_map (Minus exp1 exp2) m = (value_exp_map exp1 m) - (value_exp_map exp2 m)"
 370 
 371 textZa popunjavanje memorije koristimo funkciju 
 372 \verb!bulkload :: "'a list ⇒ (nat, 'a) mapping"! 
 373 koja datu listu preslikava u stanje u memoriji. Ovaj poziv
 374 odgovara vrednostima promenljivih koje smo koristili u prethodnom primeru.
 375 
 376 definition val_map1 :: "Memory" where
 377   "val_map1 = Mapping.bulkload [1,2,3]"
 378 
 379 text Uključujemo RBT stabla da bi moglo da izvrši računanje, teoriju
 380 RBT_Mapping. 
 381 
 382 value "value_exp_map (Mult (Var 2) (Add (Const 2) (Const 5))) val_map1"
 383 
 384 definition x5 :: "exp'" where "x5 = Add (Var 0) (Const 3)"
 385 value "value_exp_map x5 val_map1"
 386 
 387 definition x6 :: "exp'" where "x6 = Mult (Var 0) (Add (Const 2) (Const 5))"
 388 value "value_exp_map x6 val_map1"
 389 
 390 text Uvodimo novu operaciju čitanja iz memorije: 
 391 
 392 datatype Operation' = OpAdd | OpMult | OpMin | OpPush int | OpRead var_num 
 393 type_synonym Stack' = "int list"
 394 
 395 fun executeOp' :: "Operation' ⇒ Stack' ⇒ Memory ⇒ Stack'" where
 396 "executeOp' OpMult (x # y # xs) m = (x * y) # xs" | 
 397 "executeOp' OpAdd (x # y # xs) m = (x + y) # xs" | 
 398 "executeOp' OpMin (x # y # xs) m = (x - y) # xs" | 
 399 "executeOp' (OpPush x) xs m = (x # xs)" | 
 400 "executeOp' (OpRead x) xs m = ((read_memory m x) # xs)"
 401 
 402 textUvodimo program, prevođenje izraza u program i njegovo izvršavanje:
 403 
 404 type_synonym Prog' = "Operation' list"
 405 
 406 primrec translate' :: "exp' ⇒ Prog'" where
 407 "translate' (Var x) = [OpRead x]" | 
 408 "translate' (Const x) = [OpPush x]" | 
 409 "translate' (Add i1 i2) = [OpAdd] @ translate' i1 @ translate' i2" | 
 410 "translate' (Minus i1 i2) = [OpMin] @ translate' i1 @ translate' i2" | 
 411 "translate' (Mult i1 i2) = [OpMult] @ translate' i1 @ translate' i2"
 412 
 413 primrec executeProg' :: "Prog' ⇒ Stack' ⇒ Memory ⇒ Stack'" where
 414 "executeProg' [] s m = s" | 
 415 "executeProg' (op # p) s m = executeOp' op (executeProg' p s m) m"
 416 
 417 value "x5"
 418 value "value_exp_map x5 val_map1"
 419 value "translate' x5"
 420 value "executeProg' (translate' x5) [] val_map1"
 421 
 422 textNa sličan način kao u prethodna dva primera uvodimo narednu definiciju i
 423 dokazujemo da je dobro definisana:
 424 
 425 definition computer_map where 
 426 "computer_map i m = executeProg' (translate' i) [] m"
 427 
 428 lemma executeProg'_append[simp]: 
 429   shows "executeProg' (p1 @ p2) s m = executeProg' p1 (executeProg' p2 s m) m"
 430   apply (induction p1)
 431    apply auto
 432 done
 433 
 434 lemma executeProg'_translate[simp]: 
 435   shows "executeProg' (translate' i) s m = (value_exp_map i m) # s"
 436   apply (induction i arbitrary: s)
 437       apply auto
 438 done
 439 
 440 theorem "hd(computer_map i m) = value_exp_map i m"
 441   unfolding computer_map_def
 442 by simp
 443 
 444 end
 445