## Sortiranje Seznam z `n` elementi lahko enostavno sortiramo z O(n²) primerjavami, ali z nekaj truda z O(n⋅log(n)). Da pa se tudi precej slabše: tu bomo implementirali deterministični [bogosort](https://en.wikipedia.org/wiki/Bogosort), ki seznam sortira v času O(n⋅n!).
### `is_sorted/1` Definirajte predikat `is_sorted(L)`, ki velja, če so elementi seznama `L` urejeni po nepadajočem vrstnem redu. Predpostavite lahko, da seznam vsebuje samo števila; primerjamo jih lahko z operatorjem `>=`. ?- is_sorted([2,3,6,6,12]). true. ?- is_sorted([2,3,1,6,5]). false.
% implementiraj predikat is_sorted/1
is_sorted([2,3,6,6,12]).
is_sorted([2,3,1,6,5]).
### `permute/2` Definirajte predikat `permute(A, B)`, ki velja, če je seznam `B` permutacija elementov iz seznama `A`. ?- permute([1,2,3], L). L = [1,2,3] ; L = [1,3,2] ; L = [2,1,3] ; L = [2,3,1] ; L = [3,1,2] ; L = [3,2,1]. *Namig:* pomagate si lahko s predikatom `insert/3` od prejšnjič.
insert(X, L, [X|L]). insert(X, [H|L1], [H|L2]) :- insert(X, L1, L2).
% implementiraj predikat permute/2
permute([1,2,3], L).
### `bogosort/2` Definirajte predikat `bogosort(A, B)`, ki velja, če seznam `B` vsebuje elemente iz `A` v nepadajočem vrstnem redu: ?- bogosort([2,4,3,1,4], L). L = [1,2,3,4,4]. Predikat naj izvede O(n⋅n!) primerjav.
% implementiraj predikat bogosort/2
bogosort([2,4,3,1,4], L).
## Turingov stroj V čistem prologu bomo implementirali Turingov stroj. Spomnimo se, da Turingov stroj namesto RAMa uporablja neskončni *trak* celic, v katerih hrani simbole izbrane abecede (pogosto sta to `0` in `1`). *Glava* bere in piše iz trenutne celice ter se lahko pomika za eno mesto levo ali desno. V vsakem trenutku je stroj v enem od končno mnogo *stanj*. Program za Turingov stroj sestoji iz nabora ukazov. Na podlagi programa stroj na vsakem koraku glede na trenutni simbol in stanje na trak zapiše nov simbol, premakne glavo v levo ali desno (ali jo pusti na mestu) in se postavi v novo stanje (ki je lahko enako prejšnjemu). Program bomo zapisali z naborom pravil, ki ga podamo s predikatom `program/6`: program(Name, InState, InSymbol, OutState, OutSymbol, Direction) Argument `Name` pove ime programa in povezuje vsa pravila za določen program. `InState` in `InSymbol` sta stanje stroja in simbol v celici pod glavo, pri katerih uporabimo to pravilo. `OutState` pove stanje, v katerem bo stroj po tem ukazu. `OutSymbol` je nov simbol, ki naj se zapiše v trenutno celico, `Direction` pa smer, v katero naj se premakne glava.
Program, ki številu v eniškem zapisu prišteje ena: program(plus1, q0, 1, q0, 1, right). program(plus1, q0, b, final, 1, stay). ?- turing(plus1, [1,1,1], Out). Out = [1,1,1,1].
program(plus1, q0, 1, q0, 1, right). program(plus1, q0, b, final, 1, stay).
Program, ki skopira zaporedje enic: program(copy, q0, b, final, b, stay). program(copy, q0, 1, 2, b, right). program(copy, 2, b, 3, b, right). program(copy, 2, 1, 2, 1, right). program(copy, 3, b, 4, 1, left). program(copy, 3, 1, 3, 1, right). program(copy, 4, b, 5, b, left). program(copy, 4, 1, 4, 1, left). program(copy, 5, b, q0, 1, right). program(copy, 5, 1, 5, 1, left). ?- turing(copy, [1,1,1], Out). Out = [1,1,1,b,1,1,1].
program(copy, q0, b, final, b, stay). program(copy, q0, 1, 2, b, right). program(copy, 2, b, 3, b, right). program(copy, 2, 1, 2, 1, right). program(copy, 3, b, 4, 1, left). program(copy, 3, 1, 3, 1, right). program(copy, 4, b, 5, b, left). program(copy, 4, 1, 4, 1, left). program(copy, 5, b, q0, 1, right). program(copy, 5, 1, 5, 1, left).
Program, ki izračuna (doda na konec) liho pariteto: program(parity, q0, 0, q0, 0, right). program(parity, q0, 1, q1, 1, right). program(parity, q0, b, final, 0, stay). program(parity, q1, 0, q1, 0, right). program(parity, q1, 1, q0, 1, right). program(parity, q1, b, final, 1, stay). ?- turing(parity, [1,1,0,1,0,0,1], Out). Out = [1, 1, 0, 1, 0, 0, 1, 0] .
program(parity, q0, 0, q0, 0, right). program(parity, q0, 1, q1, 1, right). program(parity, q0, b, final, 0, stay). program(parity, q1, 0, q1, 0, right). program(parity, q1, 1, q0, 1, right). program(parity, q1, b, final, 1, stay).
Za končno stanje bomo uporabili atom `final`, začetno stanje pa bomo označili s `q0`.
### `action/3` »Neskončni« trak predstavimo z dvema seznamoma. Seznam `L` vsebuje simbole na traku levo od glave, seznam `R` pa simbole desno od glave (vključno s simbolom, ki je trenutno pod glavo). Poleg tega bomo zaradi lažjega dostopanja seznam `L` hranili v obratnem vrstnem redu. Če je trenutna vsebina traku npr. c d e f g h =========== ^ | `---------- glava jo torej predstavimo s seznamoma L = [e,d,c] R = [f,g,h] Prazno celico predstavimo s simbolom `b`. Celoten trak bomo predstavili s strukturo oblike `L-R`. Tukaj operator `-` ne pomeni odštevanja, temveč le povezuje seznama `L` in `R` v eno strukturo. Zgorjni trak tako opisuje struktura [e,d,c]-[f,g,h] Definirajte predikat `action(Direction, InL-InR, OutL-OutR)`, ki glavo premakne v dano smer (`Direction` je lahko `left`, `right` ali `stay`). Primer: % glava ostane na mestu ?- action(stay, [e,d,c]-[f,g,h], OutL-OutR). OutL = [e,d,c], OutR = [f,g,h]. % glava se pomakne v desno: prvi element iz desnega seznama premaknemo levo ?- action(right, []-[c,d], OutL-OutR). OutL = [c], OutR = [d]. % če je seznam v dani smeri prazen, moramo dodati nov b ?- action(left, []-[c,d], OutL-OutR). OutL = [], OutR = [b,c,d].
% implementiraj predikat action/3
action(stay, [e,d,c]-[f,g,h], OutL-OutR).
action(right, []-[c,d], OutL-OutR).
action(left, []-[c,d], OutL-OutR).
### `head_rest/3` Trenutni simbol pod glavo lahko s traku `L-R` dobimo tako, da vzamemo prvi element seznama `R`. Lahko pa se zgodi, da je `R` prazen. Definirajte pomožni predikat `head_rest(R, Head, Rest)`, ki iz trenutnega seznama na desni `R` dobi element `Head` pod glavo in preostanek seznama `Rest`. Primer: % če seznam na desni ni prazen, vrnemo prvi element in preostanek ?- head_rest([0,1,1], Head, Rest). Head = 0, Rest = [1,1]. % če je seznam na desni prazen, vrnemo prazen simbol `b` in prazen seznam ?- head_rest([], Head, Rest). Head = b, Rest = [].
% implementiraj predikat head_rest/3
head_rest([0,1,1], Head, Rest).
head_rest([], Head, Rest).
### `step/5` Definirajte predikat `step(Name, InL-InR, InState, OutL-OutR, OutState)`, ki izvede en korak na Turingovem stroju s programom `Name` pri vsebini traku `InL-InR` in stanju `InState`. Primer: ?- step(plus1, []-[1,b,b], q0, OutL-OutR, OutState). OutL = [1], OutR = [b,b], OutState = q0. ?- step(plus1, [1]-[b,b], q0, OutL-OutR, OutState). OutL = [1], OutR = [1,b], OutState = final.
% implementiraj predikat step/5
step(plus1, []-[1,b,b], q0, OutL-OutR, OutState).
step(plus1, [1]-[b,b], q0, OutL-OutR, OutState).
### `run/4` Definirajte predikat `run(Name, State, InL-InR, OutL-OutR)`, ki požene program `Name` iz začetnega stanja `State` in vhodnega traku `InL-InR` do konca: - če je v stanju `final`, se ustavi; - sicer izvede en korak in znova pokliče `run/4`.
Primer: ?- run(plus1, q0, []-[1,1,1], OutTape). OutTape = [1,1,1]-[1].
% implementiraj predikat run/4
run(plus1, q0, []-[1,1,1], OutTape).
### `turing/3` Definirajte predikat `turing(Name, InTape, OutTape)`, ki izvede program `Name` na vhodnem traku `[]-InTape`. Argument `InTape` je torej le seznam simbolov desno od glave. Tudi `OutTape` naj je navaden seznam vseh simbolov na traku v pravem vrstnem redu. Iz strukture `L-R` ga dobimo tako, da `L` obrnemo in ga staknemo z `R`. Primer: ?- turing(plus1, [1,1,1], OutTape). OutTape = [1,1,1,1].
% implementiraj predikat turing/3
turing(plus1, [1,1,1], OutTape).