Föreläsning 13 i programmeringsparadigm. Jag har ju delat ut Lite grann om syntax, semantik och syntaxanalys. Några ex finns på Nadas kursexpedidition. Labben "Danska räkneord" ska göras med en syntaxanlysator skriven med någon av de metoder som finns i häftet. Du kan välja mellan: Vårt parser-språk (byggt på Haskell, kapitel 2, sid 12). Hjälp på filen /info/progp05/parser.hs DCG (byggt på Prolog, kapitel 5 sid 35, Brna Chapter 8, p 78) "Imperativ Haskell" (kapitel 4, sid 30). Hjälp på filen /info/progp05/imphaskellparser.hs Java. (kapitel 3, sid 22) Hjälp på filen /info/progp05/terminallist.java Som avslutning några svårare exempel (svårare än tentaproblem) och för ett problem diskusion kring backtracking i Prolog och Haskell. Ett klassiskt datorprogram : Eliza. Efter Sterling och Shapiro Program 14.15. Programmen i denna bok finns på http://.informatik.uni-trier.de/~ley/books/sterling.html. Programmet eliza finns på /info/progp02/prolog/eliza.pl (men för att få det att fungera bytte jag /== till /= i lookup). Det är ett roligt program att köra. Imponerande "intellegent" för att vara ett 40-tal rader Prolog. Men inte helt lätt att förstå procedurellt om man inte är van prologprogrammerare. Ett liknande program skrevs 1966 av Weizenbaum. Han blev förskräckt av folks reaktioner på programmet och skrev en mycket bra bok om datorer och mänskligt vetande (Weizenbaum, J : Computer Poer and Human reason. 1976.) /* Körning?- eliza. : [i, am, unhappy]. Ho long have you been unhappy? : [my, father, loves, me]. Please you tell me more about father : [i, am, unhappy]. Ho long have you been unhappy? : [since, yesterday]. Please go on. : [hat, shall, i, do]. Please go on. : [i, feel, happy]. Do you often feel that ay? : [bye]. Goodbye. I hope I have helped you yes */
/* eliza :- Simulates a conversation via side effects. */ % You need to type in a list of ords such as [i, am,unhappy]. eliza :- read(input), eliza(input),!. eliza([bye]) :- reply([ Goodbye. I hope I have helped you ]). eliza(input) :- pattern(stimulus,response), % Sophisticated multi-use of match! match(stimulus,table,input), % Ny Table skapas och fylls med 1 =.., osv match(response,table,output),% Samma Table används, I Respons ersätts 1, 2 osv reply(output), % Endast procedurell läsning read(input1), % Endast procedurell läsning!, eliza(input1). % Endast procedurell läsning /* match(pattern,dictionary,words) :- Pattern matches the list of ords Words, and matchings are recorded in the Dictionary. */ match([n Pattern],Table,Target) :- integer(n), % True if N is bound to an integer lookup(n,table,lefttarget), append(lefttarget,righttarget,target), match(pattern,table,righttarget). match([word Pattern],Table,[Word Target]) :- atom(word), % True if N is bound to an atom match(pattern,table,target). match([],table,[]). /* pattern(stimulus,response) :- Response is an applicable response pattern to the pattern Stimulus. */ pattern([i,am,1],[ Ho,long,have,you,been,1,?]). pattern([1,you,2,me],[ What,makes,you,think, I,2,you,?]). pattern([i,like,1],[ Does,anyone,else,in,your,family,like,1,?]). pattern([i,feel,1],[ Do,you,often,feel,that,ay,?]). pattern([1,x,2],[ Please,you,tell,me,more,about,X]) :- important(x). pattern([1],[ Please,go,on,. ]). important(father). important(sister). important(son). important(mother). important(brother). important(daughter). reply([head Tail]) :- rite(head), rite( ), reply(tail). reply([]) :- nl. % ne line. lookup(x,[(x,v) XVs],V). lookup(x,[(x1,v1) XVs],V) :- X \= X1, lookup(x,xvs,v). /* append(xs,ys,xsys) :- XsYs is the result of concatening the lists Xs and Ys. */ append([],ys,ys). append([x Xs],Ys,[X Zs]) :- append(xs,ys,zs). Märk att lookup, trots sitt namn, kan användas för att utvidga och skapa en tabell:?- lookup(1,table, siv), lookup(2,table, eva). Table = [(1,siv),(2,eva) _A]? ; no
lookup kan förstås även användas för att slå upp i tabellen. Exempelvis om vi kompletterar den förra körningen med ett nytt delmål (sist) som slår upp värdet för 1 :?- lookup(1,table,siv), lookup(2,table,eva), lookup(1,table, Info1). Info1 = siv, Table = [(1,siv),(2,eva) _A]? ; no Annan klassiker : Placera damer på schackbräde. Från Sterling och Shapiro. Problem: Placera 8 damer på ett schackbräde så att de ej hotar varandra. Klart att varje dam måste ha en egen rad och kolumn. Vi kan representera en lösning som en permuterad lista [1..8], en lista [ med vilken rad damen i kolumn 1 står på, vilken rad damen i kolumn 2 står på,... ] Lösningen [4, 2, 7, 3, 6, 8, 5, 1 ] /* queens(n,queens) :- Queens is a placement that solves the N queens problem, represented as a permutation of the list of numbers [1,2,...,N] i. e. [ro for placement of Queen in column 1, ro for placement of Queen in column 2,... ] Finns i /info/progp02/prolog/queens.pl */ Naivt generera-test-program: queens(n,qs) :- range(1,n,ns), permutation(ns,qs), safe(qs). % -- generate ----------------- and - test - % range(m,n,ps) Ps is a list ith elements from M to N % i. e. in Haskell the list [m..n] range(m,n,[m Ns]) :- M < N, M1 is M+1, range(m1,n,ns). range(n,n,[n]). % safe(qs) :- The placement Qs is safe. /* safe([q Qs]) :- safe(qs), \+ attack(q,qs). safe([]).
attack(6, [8, 5, 1]) om attack(6, 1, [8, 5, 1) attack(6, 1, [8 [5, 1]]) om 6 is 8+1 eller 6 is 8-1 eller attack(6, 2, [5, 1] % osv rekursivt, diagonalerna tomma, blir false */ % attack(x,xs) A Queen in next column in ro X attacks some Queen in previous column placed as Xs attack(x,xs) :- attack(x,1,xs). attack(x,n,[y Ys]) :- X is Y+N. attack(x,n,[y Ys]) :- X is Y-N. attack(x,n,[y Ys]) :- N1 is N+1, attack(x,n1,ys). Modifikationer för ett mycket effektivare program: queens(n,qs) :- range(1,n,ns), queens(ns,[],qs). queens(unplacedqs,safeqs,qs) :- select(q,unplacedqs,unplacedqs1), \+ attack(q,safeqs), queens(unplacedqs1,[q SafeQs],Qs). %Ok,place ne safe queen. queens([],qs,qs). %No unplaced queen, safe queens = solution Problemet löst i Haskell (med Prologliknade körsätt) : module Queens here -- 8 queens problem -- try ::[Integer] -> [Integer] -> [Integer] -> [[Integer]] -- Unplaced Queens -> Tried Queens -> Placed Queens -> -- List of solutions -- Backtracking is represented by recursion. -- Depth-first-search try [] [] safeqs = [safeqs] -- found one solution try [] triedqs safeqs = [] -- no solution try (q:unplacedqs) triedqs safeqs attack q safeqs = res2 -- back-track otherise = res1 ++ res2 -- place + back-track here res1 = try (triedqs++unplacedqs) ([]) (q:safeqs) res2 = try (unplacedqs) (q:triedqs) (safeqs)
attack :: Integer -> [Integer] -> Bool attack x xs = att x 1 xs here att x n [] = False att x n (y:ys) = x == y+n x == y-n att x (n+1) ys -- Run as try [1..8] [] [] -- Prolog like presentation of list::[a] haskellprolog :: Sho a => ([a], String) -> IO() haskellprolog (solution : solutions, qv) = do putstr (qv ++ " = " ++ sho solution ++ "? ") c <- getline if c == "" then putstr("\nyes") else haskellprolog (solutions, qv) haskellprolog ([], qv) = putstr("\no") queens (n,qv) = (try [1..n] [] [], qv) -- A run: Queens> haskellprolog (queens(8, "Rs")) Rs = [4,2,7,3,6,8,5,1]? ; Rs = [5,2,4,7,3,8,6,1]? ; Rs = [3,6,4,2,8,5,7,1]? yes På grund av att Haskell har lat evaulering så fungerar detta program som Prolog, om vi bara vill se första lösningen så räknas bara den lösningen ut. De kan man se om vi gör :s +ts : Queens> :s +ts Queens> haskellprolog(queens(8,"rs")) Rs = [4,2,7,3,6,8,5,1]? yes :: IO () (14 397 reductions, 30 513 cells) Queens> haskellprolog(queens(8,"rs")) Rs = [4,2,7,3,6,8,5,1]? ; Rs = [5,2,4,7,3,8,6,1]? ; Rs = [3,6,4,2,8,5,7,1]? ; Rs = [3,5,2,8,6,4,7,1]? ; Rs = [5,7,1,3,8,6,4,2]? ; Rs = [4,6,8,3,1,7,5,2]? ; Rs = [3,6,8,1,4,7,5,2]? yes :: IO () (43 047 reductions, 90 897 cells)
Backtracing i Prolog och Haskell. Boxmodellen för queens/3 blir queens/3 queens(u, select/3 \+ attack/2 queens/3 select/3 \+ attack/2 queens/3 queens(u,... queens([],.. queens([],.. Prolog kommer att "back-tracka" när \+ attack misslyckas, och söktädet blir förgrenat när vi gör Redo på select och vi kommer försöka med en ny dam i en kolonn. Ibland kommer att till slut, när \+ attack lyckas för 8 unifieringar av olika Q, listan med oplacerade damer bli tom och vi har nått ett löv i sökträdet. Besvaras förslaget med ; så backar vi till närmaste select och får ett nytt förslag på ett Q. Backar vi till ett selectsom med slut på oplacerade damer backar vi vidare till ett select längre till vänster. try fungerar på samma sätt; val av en dam motsvaras av att vi mönstermatchar fram en ny dam från listan med oplacerade damer så länge det finns oplacerade damer. q:unplacedqs motsvarar alltså UnplacedQs och select(q,unplacedqs,..) i queens i Prolog. För att kunna starta om har vi en extra parameter triedqs med damer som vi redan provat. I både Haskell och Prolog accumulerar vi placerade damer i parametrarna safeqs respektive SafeQs. I Haskell blir en lösning ett resultat för try en lista med 8 damer [safeqs] och en frånvaro av lösning en tom lista []. Förgreningen sker pga att vi har förgrenande rekursion: res1 ++ res2 dvs try (triedqs++unplacedqs) [] (q:safeqs) ++ try (unplacedqs) (q:triedqs) (safeqs) Som redan påpekats: På grund av att Haskell har lat evaulering så fungerar körningen under haskellprolog som Prolog. Haskell är lat och räknar inte ut någon del av sökträdet som inte efterfrågas. Prolog måste som vanligt släpa med en extra parameter för resultat av annan typen än Bool. Problem av denna typ brukar räknas som lite svårare i andra paradigmer än den logiska. Men lär man sig hur Prolog funkar så blir man så småningom rätt bra att lösa problemen i t ex Haskell också. Kunskaper om en paradigmer gör att man blir bättre programmerare i andra paradigmer.
En trimmad Sudoki-lösning. select(x, [X Xs], Xs). select(x, [Y Ys], [Y Zs]) :- select(x, Ys, Zs). ninecheck(xs) :- select(1,xs, Xs1), select(2,xs1, Xs2), select(3,xs2, Xs3), select(4,xs3, Xs4), select(5,xs4, Xs5), select(6,xs5, Xs6), select(7,xs6, Xs7), select(8,xs7, Xs8), Xs8 = [9]. nxncheck([]). nxncheck([n Ns]):- ninecheck(n),nxncheck(ns). % sort(xs,ys) :- The list Ys is an ordered permutation of the Xs. quicksort([x Xs],Ys) :- partition(xs,x,littles,bigs), quicksort(littles,ls), quicksort(bigs,bs), append(ls,[x Bs],Ys). quicksort([],[]). partition([x Xs],Y,[X Ls],Bs) :- lesst(x,y),!, partition(xs,y,ls,bs). partition([x Xs],Y,Ls,[X Bs]) :- partition(xs,y,ls,bs). partition([],_,[],[]). lesst(list1, List2) :- avar(list1, I1), avar(list2, I2), I1 =< I2. avar([], 0) avar([x Xs], I) :- var(x),!, avar(xs, J), I is J +1. avar([_ Xs], I) :- avar(xs, I). % var(x) är sant om X fortfarande inte unifieras när vi försöker detta mål vid körning. var(x) har bara procedurell läsning. % Låt oss slå ihop alla tre 9x9-grupper (alla rader, alla kolonner, och alla delkvadrater så vi får en lista med 27 st 9-listor Sx. Om vi sortera Sx till Sy så att de 9-grupper som har många element somredan är tal så borde sökträcen bli mycket mindre. xcheck(s) :- trans(s,st,sg), append(s, St, Sa), append(sa, Sg, Sx), quicksort(sx, Sy),nXnCheck(Sy). Detta visar sig också vara fallet; lösningen fån föreläsning 11 klarar ca 17 saknade tal på rimlig tid men nu kan man pröva med ca 40 obekanta. men för att klara tidningarnas problem måste vi nog tänka lite till. När vi nu trimmat lösningen har vi inte kunna nöja oss med den logiska lösningen. Dessutom har vi genom att tänka procedurellt tänkt på ett visst körfall; den trimmade lösningen är inget framsteg för att kolla en given sudoku.
Prologdelen av fjolårs-tentan: 6. a) Skriv ett predikat för select(x,harxs,ettmindrexs) med betydelsen att listan EttMindreXs är resultatet av att en förekomst av X har tagits bort från HarXs. b) Skriv ett predikat för member(element,list) med betydelsen Element är ett element i listan List med hjälp av select. c) Skriv ett predikat för choosetofrom(list, E1, E2) med betydelsen att E1 och E2 är två olika element i listan List med hjälp av select och member. (10p) 7. Skriv i Prolog ett generate-and-test-program som löser följande problem: I en kappsegling deltar 7 båtar, bland annat Patricia. Spray kom precis innan Necesse. Endast Volo placerade sig precis mellan Maybee och Bonnie. Exakt en båt placerade sig mellan Spray och Maybee. Bigfoot kom precis mitt i resultatlistan. Hur ser resultatlistan ut? 12p) 8. Prologs semantik (procedurell läsning) kan beskrivas med Byrds boxmodel. a) Kommer vi till en box via Redo-porten kommer vi 1) alltid att lämna boxen via Fail-porten. X) alltid att lämna boxen via Call-porten. 2) att lämna boxen via Fail-porten eller via Exit-porten b) Kommer vi till en box via Call-porten kommer vi 1) alltid att lämna boxen via Fail-porten. X) alltid att lämna boxen via Exit-porten. 2) att lämna boxen via Fail-porten eller via Exit-porten. c) Kommer vi till en box via Redo- porten har vi 1) ofta lämnat efterföljande box via Call-porten. X) ofta lämnat efterföljande box via Exit-porten. 2) ofta lämnat efterföljande box via Fail-porten. d) Har vi lämnat en Exit-port och sedan passerat ett snitt (cut,!) 1) kommer vi in i efterföljande box (när sådan finns) via Call-porten. X) hoppar vi över efterföljande box (när sådan finns). 2) startar en "backtracking" e) Har vi lämnat en Fail-port och sedan passerat ett snitt (cut,!) 1) kommer vi in i föregående box (när sådan finns) via Redo-porten. X) kommer vi in i föregående box (när sådan finns) via Call-porten. 2) kommer vi inte in i föregående box (när sådan finns) via Redo-porten. 8p) 9 När man kör i Prolog-fönstret i emacs, vad skulle följande körningar (unifieringar) ge för resultat (Lyckas unifieringarna? Vilka substitutioner görs i så fall?)?- a= b.?- A= b.?- a= B.?- A= B.?- m(adam, rut) = m(adam, rut).?- m(adam, rut) = m(adam, rut).?- m(adam, rut) = m(adam, Adam).?- m(adam, rut) = m(adam, Rut).?- m(adam, rut) = m(rut, Rut).?- m(adam, rut) = M(adam, rut). (10p)