Presentation laddar. Vänta.

Presentation laddar. Vänta.

Haskell En större tillämpning: tolk för Turingmaskiner.

Liknande presentationer


En presentation över ämnet: "Haskell En större tillämpning: tolk för Turingmaskiner."— Presentationens avskrift:

1 Haskell En större tillämpning: tolk för Turingmaskiner

2 Turingmaskiner Du har bekantat dig med Turingmaskiner på Intro till DT/DV, men vi ska repetera lite: • En Turingmaskin består av en mängd av oordnade instruktioner av följande struktur: (state, symbol, next symbol, next state, direction) • Data består av en sträng som kommer från ett alfabet där ’ ’ alltid ingår. Ett typiskt alfabet är {’ ’, ’0’, ’1’}. • Bandet där data finns är oändligt både till vänster och höger (det finns oändligt med blankor till både vänster och höger om datadelen).

3 Turingmaskiner • I början ligger läs- och skrivhuvudet vid första icke-blanka tecknet till vänster. Maskinen befinner sig i tillstånd 1. • När en instruktion ska utföras, sker två saker: • Man skriver ett nytt tecken på bandet vid skrivhuvudet (det får vara samma tecken som det nuvarande) • Läs- och skrivhuvudet rör sig ett steg mot vänster eller höger (L/R) • När man väljer nästa instruktion som ska utföras, ser man till Turingmaskinens aktuella tillstånd och det aktuella tecknet. • Nu söker man i instruktionsrepertoiren efter en instruktion som motsvarar dessa. Repertoiren måste skrivas så att det finns max. en sådan instruktion. När inget hittas, stannar maskinen.

4 Turingtolk bottom up • I vårt (lite) större exempel ska vi designa och koda en tolk som klarar av att exekvera Turingmaskiner. • Vi kommer att definiera problemet bottom up, med olika utmaningar: • Modellera data (bandet) • Modellera själva Turingmaskinerna (instruktionerna) • Definiera de funktioner vi behöver för att skapa ett band och operera på det.

5 Turingtolk bottom up... Definiera de funktioner vi behöver för att skapa ett band och operera på det: • makeTape • moveHead • readFromTape • writeToTape • show (för att se utskriften av ett band) • getInstruction (för att söka nästa giltiga instruktion) • runTM (för att exekvera en given Turingmaskin)

6 Turingtolk: modellera bandet Krav: • Bandet ska bestå av celler som innehåller antingen en symbol ur det givna alfabetet eller ’ ’. • Bandet ska vara oändligt till både vänster och höger. Lösning: • Ett band består av en lista av de tecken som kommer före den nuvarande positionen, det tecken som läshuvudet för tillfället befinner sig vid, samt en lista på de tecken som kommer efter den nuvarande positionen.

7 Turingtolk: modellera bandet • Implementation: type Symbol = Char data Tape = Tape [Symbol] Symbol [Symbol] Haskell-nytt: type namnger en datatyp och skapar en typsynonym. data skapar en s k algebraisk datatyp och dess konstruktorer. Stor begynnelsebokstav är ett måste.

8 Turingtolk: modellera turingmaskinerna • Applikationsprogrammen, dvs de egentliga Turingmaskinerna, ska bestå av mängder av instruktionerna med det givna formatet. • Implementation: en lista av tupler: data Direction = L | R type State = Integer -- (tillstånd, tecken, nästa tecken, nästa -- tillstånd, riktning) type Instruction = (State, Symbol, Symbol, State, Direction) {- Ändrar ettorna till 0 -} m1 = [ (1, '0', '0', 1, R), (1, '1', '0', 1, R) ]

9 Funktionerna: makeTape • Ska skapa ett band givet en sträng som ska representera data på bandet. • Implementation: {- En hjälpfunktion för att skapa ett band som pekar på det första tecknet i en given sträng. -} mkTape :: String -> Tape mkTape (x:xs) = Tape [] x xs x xs

10 Funktionerna: makeTape mkTape :: String -> Tape mkTape (x:xs) = Tape [] x xs Ex. *Main> mkTape "11011" [1] x xs Obs! Utskriften produceras av implementationen av Show.

11 Funktionerna: moveHead • Insikt: för att ge illusionen av ett oändligt band sätts automatiskt nya blanktecken till och tas bort om man flyttar läshuvudet utanför den del av bandet som innehåller någon data. • OBS! Man tänker här läs- och skrivhuvudet som en lucka där exakt ett tecken syns åt gången. • I stället för att föra läs- och skrivhuvudet åt höger, “drar” man bandet ett steg åt VÄNSTER så att ett nytt tecken (dvs nästa tecken åt höger) blir synligt i luckan

12 Funktionerna: moveHead • Det finns ett antal olika fall att beakta: • Man befinner sig i början av data • Man befinner sig i slutet av data • man befinner sig i mitten av data • Dessa måste hanteras separat för både L och R

13 Funktionerna: moveHead • OBS! Eftersom man inte har direkt access via ex. indexering till tecknena på bandet, dvs listelementen, kan de INTE hanteras på samma sätt på den vänstra och den högra bandhalvan, ex. med pekare eller index. • Bandet till höger om luckan motsvarar den konceptuella bilden av bandet (tecknen kommer i samma ordning som vi skulle rita dem på papper). • Den vänstra bandhalvan ger access till listans första element via cons (:). Det är alltså elementet som står FÖRST i den vänstra listhalvan som tolkas vara tecknet till vänster av skrivhuvudet/luckan osv Implementation: [ ‘1’, ‘1’, ‘0’, ‘1’] ‘0’ [‘1’, ‘1’, ‘0’]

14 Funktionerna: moveHead R moveHead :: Direction -> Tape -> Tape • Läs- och skrivhuvudet före första tecknet i data: moveHead R (Tape [] ' ' (r:rs)) = Tape [] r rs Implementation: [ ] ‘ ’ [‘1’, ‘1’, ‘0’, ‘1’, ‘1’] [ ] ‘1’ [‘1’, ‘0’, ‘1’, ‘1’]

15 Funktionerna: moveHead R moveHead :: Direction -> Tape -> Tape • Slutet av data, vid sista tecknet: moveHead R (Tape ls c []) = Tape (c:ls) ' ' [] Implementation: [ ‘1’, ‘0’, ‘1’, ‘1’] ‘ 1’ [ ] – Obs! Spegelbilden! [ ‘1’, ‘1’, ‘0’, ‘1’, ‘1’] ‘ ’ [ ] – Obs! Spegelbilden!

16 Funktionerna: moveHead R moveHead :: Direction -> Tape -> Tape • Normalfallet, i mitten av data: moveHead R (Tape ls c (r:rs)) = Tape (c:ls) r rs Implementation: [ ‘1’, ‘1’] ‘ 0’ [ ‘1’, ‘1’] – Obs! Spegelbilden! [ ‘0’, ‘1’, ‘1’] ‘1 ’ [‘1’ ] – Obs! Spegelbilden!

17 Funktionerna: moveHead L moveHead :: Direction -> Tape -> Tape • Vid slutet av data vid första blanktecken: moveHead L (Tape (l:ls) ' ' []) = Tape ls l [] Implementation: [ ‘1’, ‘1’, ‘0’, ‘1’, ‘0’] ‘ ’ [ ] – Obs! Spegelbilden! [ ‘1’, ‘0’, ‘1’, ‘0’] ‘1 ’ [ ] – Obs! Spegelbilden!

18 Funktionerna: moveHead L moveHead :: Direction -> Tape -> Tape • Vid början av data, vid den första icke-blanka symbolen: moveHead L (Tape [] c rs) = Tape [] ' ' (c:rs) Implementation: [ ] ‘ 0 ’ [‘1’, ‘0’, ‘1’, ‘1’ ] [ ] ‘ ’ [‘0’, ‘1’, ‘0’, ‘1’, ‘1’ ]

19 Funktionerna: moveHead L moveHead :: Direction -> Tape -> Tape • Normalfallet, vid mitten av data: moveHead L (Tape (l:ls) c rs) = Tape ls l (c:rs) Implementation: [‘1’, ‘0’ ] ‘ 1 ’ [‘0’, ‘1’, ‘1’ ] – Obs! Spegelbild! [‘0’ ] ‘ 1 ’ [‘1’, ‘0’, ‘1’, ‘1’ ]

20 Funktionerna:read & write readFromTape :: Tape -> Symbol readFromTape (Tape _ c _) = c *Main> readFromTape (Tape ['1'] '0' ['1', '1']) '0' writeToTape :: Symbol -> Tape -> Tape writeToTape c (Tape ls _ rs) = Tape ls c rs *Main> writeToTape '1' (Tape ['1'] '0' ['1', '1']) 1[1]

21 Turingtolk: utskrift (Show / show) • Visar det resulterade bandet korrekt. • Reverserar den vänstra bandhalvan (datadelen) instance Show Tape where show (Tape ls c rs) = (reverse ls) ++ ('[':c:']':rs) *Main> show (mkTape " ") "[1] " Haskell-nytt: instance definierar en instans av en klass (polymorfism!). where anger lokala definitioner i en ekvation. Haskell-nytt: instance definierar en instans av en klass (polymorfism!). where anger lokala definitioner i en ekvation.

22 runTM och getInstruction • Nu står vi ändligen i beråd att börja använda tolken för att exekvera en Turingmaskin. • Detta innebär att vi ska hämta en instruktion och utföra den, hämta nästa instruktion och utföra den osv tills maskinen stannar i brist på en matchande instruktion. • runTM är huvudfunktionen, den som kör maskinen. • getInstruction hämtar nästa matchande instruktion från instruktionsmängden. • För att klara av valet av rätt instruktion används hjälpfunktionen find som importeras från Data.List : find :: (a -> Bool) -> [a] -> Maybe a -- Defined in `Data.List' • Väljer första värdet (ifall det finns ett sådant) som uppfyller villkoret ur en lista.

23 Data.List.find • ” The find function takes a predicate and a list and returns the first element in the list matching the predicate, or Nothing if there is no such element. “findNothing find :: (a -> Bool) -> [a] -> Maybe a -- Defined in `Data.List' Haskell-nytt: Maybe används för att signalera att funktionen inte kan producera ett meningsfullt resultat för varje fall. data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show) data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show)

24 getInstruction • getInstruction hämtar nästa matchande instruktion från instruktionsmängden. getInstruction :: [Instruction] -> (State, Symbol) -> Maybe Instruction getInstruction instrs (state, symbol) = find instrMatcher instrs where instrMatcher (state', symbol', _, _, _) = state == state' && symbol == symbol' -- instrMatcher definieras lokalt i where-satsen så att det blir predikatet som ska tillämpas på listelementen för att se ifall elem. fyller villkoret (i detta fall innebär det att vi söker efter en tupel med ett givet state och en given symbol) -- 'prim' anger state och symbol i listan av instruktioner, dessa jämförs med det state och den symbol vi söker efter.

25 runTM • Vi ska hämta en instruktion och utföra den, hämta nästa instruktion och utföra den osv tills maskinen stannar i brist på en matchande instruktion. • runTM är huvudfunktionen, den som kör maskinen. runTM :: [Instruction] -> (Tape, State) -> (Tape, State) runTM instrs (tape, state) = case (getInstruction instrs (state, readFromTape tape)) of Nothing -> (tape, state) -- startläget Just (_, _, symbol', state', dir) -> runTM instrs (moveHead dir (writeToTape symbol' tape), state')

26 runTM – vad händer här? runTM :: [Instruction] -> (Tape, State) -> (Tape, State) • Givet en lista av instruktioner (= en Turingmaskin) och en Tape och ett starttillstånd produceras ett (potentiellt ändrat) Tape och ett sluttillstånd. runTM instrs (tape, state) = case (getInstruction instrs (state, readFromTape tape)) of • Först söks den instruktion i mängden som motsvarar (current) state, current symbol (returneras av readFromTape tape ). • Alternativ 1 i case är Nothing (ingen instruktion hittas, maskinen stannar); då returnerar vi samma utgångsläge (tape, state): Nothing -> (tape, state) -- startläget

27 runTM – vad händer här? runTM :: [Instruction] -> (Tape, State) -> (Tape, State) • Alternativ 2 i case är Just och ett tupelmönster: Just (_, _, symbol', state', dir) -> • De två första elementen spelar ingen roll längre, de behövdes bara för matchningen. Därmot är resten viktiga: • symbol’ (next symbol) • state’ (next state) • dir (nästa steg L/R)... för det följande, rekursiva anropet: runTM instrs (moveHead dir (writeToTape symbol' tape), state') Producerar en Tape: moveHead :: Direction -> Tape -> Tape Producerar en Tape (med den nya symbolen “i rutan”)

28 runTM – vad händer här? runTM instrs (moveHead dir (writeToTape symbol' tape), state') • writeToTape symbol’ tape ser till att den nya symbolen skrivs på den aktuella cellen (den som är under läs- och skrivhuvudet) • moveHead dir xxx (där xxx representerar det ändrade bandet) flyttar nu läs- och skrivhuvudet (rutan) ett steg i den angivna riktningen. •runTM instr (yyy, state’) (där yyy representerar bandet efter steget) tar nu hand om det följande rekursiva anropet. runTM :: [Instruction] -> (Tape, State) -> (Tape, State) Producerar en Tape: moveHead :: Direction -> Tape -> Tape Producerar en Tape (med den nya symbolen “i rutan”) xxx yyy

29 Exempelkörningar: {- Spola till höger, dvs över bandet, utan att ändra något -} m0 = [ (1, '0', '0', 1, R), (1, '1', '1', 1, R) ] main0 = putStrLn $ show $ runTM m0 (mkTape "111000", 1) *Main> main0 (111000[ ],1) Med applikationsoperatorn $ (alt-4 på Mac) kan du undvika parenteser. Med applikationsoperatorn $ (alt-4 på Mac) kan du undvika parenteser. Applikationsoperatorn $ är högerassociativ och har prioritet 0.

30 Exempelkörningar: {- Spola först till höger, dvs över bandet, utan att ändra något, sedan tillbaka -} m00 = [ (1, '0', '0', 1, R), (1, '1', '1', 1, R), (1, ' ', ' ', 2, L) -- vänder (tillstånd 2 = åt vänster), (2, '0', '0', 2, L), (2, '1', '1', 2, L), (2, ' ', ' ', 3, R) ] -- sluttillståndet main00 = putStrLn $ show $ runTM m00 (mkTape "111000", 1) *Main> main00 ([1]11000,3)

31 Exempelkörningar: {- Ändrar ettorna till 0 -} m1 = [ (1, '0', '0', 1, R), (1, '1', '0', 1, R) ] main1 = putStrLn $ show $ runTM m1 (mkTape "111", 1) *Main> main1 (000[ ],1)

32 Exempelkörningar: {- Lägger till 001 i slutet av ett binärt tal och spolar tillbaka läshuvudet till startpositionen -} m2 = [ (1, '0', '0', 1, R), (1, '1', '1', 1, R), (1, ' ', '0', 2, R) -- slutet: första 0, (2, ' ', '0', 3, R) -- andra 0, (3, ' ', '1', 4, L) och 1. Vänder..., (4, '0', '0', 4, L), (4, '1', '1', 4, L), (4, ' ', ' ', 5, R) ] och är i början. main2 = putStrLn $ show $ runTM m2 (mkTape "111", 1) *Main> main2 ([1]11001,5)

33 Exempelkörningar: {- Ser till att antalet 1:or i en binär sträng är delbart med 3. Sätter två bitar till slutet av strängen för detta. -} m3 = [ (1, '0', '0', 1, R), (1, '1', '1', 3, R), (2, '0', '0', 2, R), (2, '1', '1', 1, R), (3, '0', '0', 3, R), (3, '1', '1', 2, R), (1, ' ', '0', 4, R), (4, ' ', '0', 42, R), (2, ' ', '1', 4, R), (5, ' ', '1', 42, R), (3, ' ', '1', 5, R) ] main3 = putStrLn $ show $ runTM m3 (mkTape "111001", 1) *Main> main3 ( [ ],42)

34 Exempelkörningar: {- Producerar en kopia av en binär sträng. Ett ‘ ‘ emellan. -} m4 = [ (1, '0', 'X', 2, R), (2, '0', '0', 2, R), (2, '1', '1', 2, R), (2, ' ', ' ', 3, R), (3, '0', '0', 3, R), (3, '1', '1', 3, R), (3, ' ', '0', 4, L), (4, '0', '0', 4, L), (4, '1', '1', 4, L), (4, ' ', ' ', 4, L), (4, 'X', '0', 1, R), (4, 'Y', '1', 1, R), (1, '1', 'Y', 5, R), (5, '0', '0', 5, R), (5, '1', '1', 5, R), (5, ' ', ' ', 6, R), (6, '0', '0', 6, R), (6, '1', '1', 6, R), (6, ' ', '1', 4, L)] main4 = putStrLn $ show $ runTM m4 (mkTape "111", 1) *Main> main4 (111[ ]111,1) *Main> putStrLn $ show $ runTM m4 (mkTape "111001", 1) (111001[ ]111001,1) *Main> putStrLn $ show $ runTM m4 (mkTape " ", 1) ( [ ] ,1)


Ladda ner ppt "Haskell En större tillämpning: tolk för Turingmaskiner."

Liknande presentationer


Google-annonser