summaryrefslogtreecommitdiff
path: root/prolog
diff options
context:
space:
mode:
authorAleksander Sadikov <aleksander.sadikov@fri.uni-lj.si>2016-02-29 13:49:20 +0100
committerAleksander Sadikov <aleksander.sadikov@fri.uni-lj.si>2016-02-29 13:49:20 +0100
commit4ef3db191d3b1f828d9db5746be9a1ff78c618e9 (patch)
tree8f341c76a88ff5da038ce32ee74efbcc018d0d74 /prolog
parent2d9a7b4323c8f78495ce45917cefdb502c846bbc (diff)
parent7335a43bbbb82de66132f12e504f2fbea736a45c (diff)
Merge branch 'master' of ssh://192.168.15.97/codeq-problems
Diffstat (limited to 'prolog')
-rw-r--r--prolog/intro_sl.html135
-rw-r--r--prolog/problems/family_relations/intro_sl.html378
-rw-r--r--prolog/problems/family_relations/sl.py9
-rw-r--r--prolog/sl.py5
-rw-r--r--prolog/style.css60
5 files changed, 583 insertions, 4 deletions
diff --git a/prolog/intro_sl.html b/prolog/intro_sl.html
new file mode 100644
index 0000000..e56e079
--- /dev/null
+++ b/prolog/intro_sl.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html lang="sl">
+ <head>
+ <meta charset="utf-8" />
+ <title>CodeQ: Prolog</title>
+ <link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body>
+
+<h1><span class="codeq">CodeQ</span>: Prolog</h1>
+<p>
+V oknu, ki ga sedaj bereš, je nekaj praktičnih napotkov in teorije za vsak učni
+sklop. Ob tem je tudi seznam nalog tega sklopa. Naloge lahko načeloma rešuješ v
+poljubnem vrstnem redu, so pa tipično urejene po naraščajoči težavnosti.
+Nekatere naloge tudi lažje rešiš z uporabo rešitev predhodnih nalog. Teh
+rešitev ne potrebuješ prepisovati, sistem si jih zapomni sam od sebe in jih
+lahko takoj uporabljaš, če želiš.
+</p>
+
+<h2>Kako rešujem naloge?</h2>
+<p>
+Nalogo začneš reševati s klikom nanjo. S tem se odpre spletna stran, v kateri
+programiraš rešitev. Vsaka stran je povezana s konkretno nalogo in vsebuje
+štiri razdelke.
+Razpored razdelkov lahko spremeniš v nastavitvah.
+</p>
+<ul>
+ <li>
+ <p>
+Navodila, ki na kratko opišejo nalogo. Posebej bodi pozoren na ime predikata,
+ki ga moraš sprogramirati – poimenovati ga moraš točno tako, kot je navedeno,
+da bo sistem razpoznal tvojo rešitev.
+Seveda pa lahko sprogramiraš več pomožnih predikatov; tu ni nobenih omejitev.
+ </p>
+ <p>
+Poleg tega bodi pozoren tudi na število argumentov, ki jih ciljni predikat
+zahteva. Npr. predikat <code>mother(?X, ?Y)</code> zahteva dva argumenta,
+<code>X</code> in <code>Y</code>. Vprašaj pred imenom argumenta pomeni, da ta
+argument lahko predstavlja tako vhod kot izhod. Argumenti, ki delujejo le kot
+vhod, so označeni s <code>+</code>, argumenti, ki delujejo le kot izhod, pa z
+<code>-</code>.
+Na začetku se ne obremenjuj s tem, ampak preprosto programiraj, vhod/izhod se
+bo večinoma pravilno uredil sam od sebe. Prolog je pameten. 😉
+ </p>
+ </li>
+
+ <li>
+ <p>
+V levem razdelku, ti bo aplikacija dajala povratne informacije. Te so lahko v
+obliki splošnega plana (kako se neke naloge lotiti), ali pa specifični nasveti,
+kaj je morda narobe s tvojo <em>trenutno</em> rešitvijo.
+Namigi so različni, včasih bodo tudi v obliki protiprimera – konkretnega vhoda,
+na katerem tvoj program deluje narobe. Tukaj bodo tudi prologova opozorila in
+obvestila o napakah v programu.
+ </p>
+ <p>
+Če se ti kakšen namig zdi napačen, najprej dobro poglej svoj program.
+Če se ti še vedno zdi napačen, nas obvesti.
+Če bo res napačen, plačamo kavo mi, sicer jo plačaš ti! 😉
+ </p>
+ <p>
+(<em>Disclaimer</em>: včasih ni možno z gotovostjo ugotoviti, ali je neka
+napaka prisotna; takrat sistem uporabi besede, kot so <em>mogoče</em>,
+<em>morda</em> in <em>verjetno</em> – tega ne štejemo kot napačno.)
+ </p>
+ </li>
+
+ <li>
+ <p>
+Konzola, tipično črne barve in kjer te čaka prologov poziv <code>?-</code>, je
+namenjena tvojemu pogovoru s prologom. V ozadju teče minimalno okrnjen
+<a target="_blank" href="http://www.swi-prolog.org">SWI Prolog</a>,
+ki mu lahko zastavljaš vprašanja.
+Vse potrebne podatke za posamezno nalogo (npr. bazo znanja) ima sistem že
+naložene. Prav tako vedno samodejni naloži trenutno različico tvojega programa.
+Prologu lahko zastavljaš poljubna vprašanja, ne le ta, povezana s tvojim
+programom.
+ </p>
+ </li>
+
+ <li>
+ <p>
+Nazadnje je tu še osrednji del: urejevalnik besedila, v katerem pišeš svojo
+rešitev. Na prvi pogled morda res manjkajo reči, kot je npr. „autocomplete“, a
+boš hitro ugotovil, da so programi v prologu kratki in ne zahtevajo veliko
+tipkanja.
+ </p>
+ <p>
+Če tvoj program prerase 8‒10 vrstic, si ga verjetno preveč zakompliciral! 😉
+Najdaljši program v vseh sklopih nalog ne potrebuje več kot kakšnih 12 vrstic
+(in takšne naloge so redke). Prej omenjeni gumbi pa so opisani spodaj.
+ </p>
+ </li>
+</ul>
+
+<p>
+Osrednji programerski del vsebuje tudi gumba „Plan“ in „Testiraj“.
+Najpogosteje boš uporabljal slednjega, ki sproži samodejno preverjanje tvoje
+rešitve.
+Gumb „Plan“ ti da splošen nasvet, kako se lotiti dane naloge; včasih ga lahko
+pritisneš večkrat, za zmeraj bolj „izdajalske“ nasvete.
+Seveda pa poskusi vsako nalogo najprej rešiti brez uporabe namigov.
+</p>
+
+<p>
+Če program ni pravilen, ti bo <span class="codeq">CodeQ</span> poleg sporočila
+o opravljenih testih včasih ponudil gumb „Namig“.
+Ta gumb ti poda namig, kaj je morda narobe s tvojim programom.
+Nekateri namigi so pripravljeni ročno s strani avtorjev aplikacije, včasih bo
+na delu umetna inteligenca, včasih boš dobil protiprimer, na katerem program ne
+deluje, včasih pa namiga sploh ne bo.
+V tem primeru je program najbrž grozen! Malce se hecam… morda. 😉
+</p>
+
+<h2>Kako shranim rešitve?</h2>
+<p>
+Sistem <span class="codeq">CodeQ</span> avtomatsko shranjuje tvoje rešitve na
+strežnik, seveda pa moraš biti povezan na internet.
+Vse spremembe se beležijo v realnem času, zato ne potrebuješ skrbeti za
+shranjevanje kode ali se obremenjavati s tem kdaj ti bo potekla seja.
+Trajanje seje je načeloma 60 minut, prekine pa se tudi, če se na drugem
+računalniku ali v drugem zavihku prijaviš z istim uporabniškim imenom.
+</p>
+<p>
+Opozorilo: glede na to, da sistem v realnem času shranjuje vse, kar v njem
+pišeš (tako je, keylogger je!), priporočamo, da se zaupnih sporočil ne piše
+vanj. Sicer jih bomo prebrali, obljubimo! 😊
+</p>
+<p>
+Vse svoje rešitve za posamezni sklop si lahko ogledaš s klikom na povezavo ob
+naslovu.
+</p>
+
+</body>
+</html>
diff --git a/prolog/problems/family_relations/intro_sl.html b/prolog/problems/family_relations/intro_sl.html
new file mode 100644
index 0000000..0e1147b
--- /dev/null
+++ b/prolog/problems/family_relations/intro_sl.html
@@ -0,0 +1,378 @@
+<!DOCTYPE html>
+<html lang="sl">
+ <head>
+ <meta charset="utf-8" />
+ <title>Prolog: družinske relacije</title>
+ <link rel="stylesheet" type="text/css" href="../style.css" />
+ </style>
+</head>
+<body>
+
+<h1>Prolog: družinske relacije</h1>
+<p>
+Prvi sklop je seveda namenjen spoznavanju prologa, obenem pa bomo obnovili naše
+poznavanje družinskih relacij – tako je, ukvarjali se bomo s tetami, strici,
+dedki, predniki in potomci.
+</p>
+
+<h2>Baza znanja</h2>
+<p>
+Načeloma je vse, kar prolog ve, zapisano v njegovi bazi znanja. Ta se tipično
+naloži v sistem iz ene ali več datotek, <span class="codeq">CodeQ</span> to
+bazo naloži samodejno. Baza pravzaprav ni nič drugega kot (preprost) prologov
+program. Spodnja slika prikazuje bazo za ta sklop nalog.
+</p>
+
+<figure>
+ <a href="famrel.svg" target="_blank">
+ <img src="famrel.svg" />
+ </a>
+ <figcaption>Graf (gozd) družinskih relacij</figcaption>
+</figure>
+
+<p>
+Baza seveda ni v grafični obliki – to je za nas, ljudi. Za prolog pa (njen
+izsek) izgleda takole:
+</p>
+<pre>
+parent(tina, william).
+parent(thomas, william).
+parent(thomas, sally).
+parent(thomas, jeffrey).
+parent(william, vanessa).
+…
+female(tina).
+female(sally).
+female(vanessa).
+…
+male(william).
+male(thomas).
+male(jeffrey).
+…
+</pre>
+
+<p>
+Kot vidiš, ni težko razbrati formata. In res je, to je povsem legalen prologov
+program. Formalno bomo taki obliki stavka (v vsaki vrstici zgoraj je en stavek)
+rekli dejstvo. V naši bazi imamo definirane tri relacije:
+</p>
+<ol>
+ <li>kdo je komu starš: relacija <code>parent/2</code>,</li>
+ <li>kdo je ženskega spola: relacija <code>female/1</code> in</li>
+ <li>kdo je moškega spola: relacija <code>male/1</code>.</li>
+</ol>
+<p>
+Zadnji dve bi lahko definirali seveda tudi drugače, npr. kot
+<code>gender(tina, female)</code>; to je povsem stvar sloga.
+</p><p>
+Ko je prolog bazo prebral, ve vse, kar je v njej zapisano. Obenem ve samo to,
+kar je v njej zapisano, in nič drugega! Pozna torej relacije <code>parent/2</code>, <code>female/1</code>
+in <code>male/1</code>. Zakaj jih pišem na tak način? Pred poševnico je ime relacije (kot
+npr. ime funkcije ali procedure v kakšnem drugem jeziku), za poševnico pa
+število argumentov. To je standarden zapis, ki ga boš videl v literaturi.
+</p><p>
+Opazil si, da se je zgoraj vsak stavek končal s piko. Pika seveda označuje
+konec stavka, tako kot v slovenščini. Vsi stavki, ne glede na tip (trije tipi
+so, jih bomo spoznali v kratkem) se končajo s piko! (Klicaj pomeni nekaj
+drugega in tega si danes še ne želiš vedeti, verjemi mi.) Verjetno bo danes
+tvoja najpogostejša napaka, da boš pozabil na piko (navada iz drugih jezikov).
+Še posebej pa bodi pozoren, da stavka ne končaš s podpičjem, to pomeni nekaj
+povsem drugega! 😉
+</p>
+
+<h2>Prvi koraki</h2>
+<p>
+Baza je torej naložena, tako da lahko začnemo z delom v prologu. Navadno to
+poteka tako, da sistemu postavljamo vprašanja. Če odpreš stran s poljubno
+nalogo, lahko v konzolo pišeš primere, ki so opisani v nadaljevanju.
+</p><p>
+Za začetek lahko prologu zastavimo preprosto vprašanje:
+</p>
+<pre>
+?- parent(tina, william).
+yes.
+</pre>
+<p>
+To vprašanje bi se v slovenščini glasilo: je Tina starš od Williama? Prolog iz
+baze znanja ve, da je Tina res starš od Williama, zato odgovori s
+<code>true</code> ali <code>yes</code> (odvisno od dialekta prologa). Gotovo si
+opazil tudi piko na koncu vprašanja. Tako je, čeprav gre za vprašalni tip
+stavka (drugi tip, ki smo ga pravkar spoznali), je na koncu pika. Vprašaj je na
+začetku, skoraj kot v španščini. 😊
+</p><p>
+Vprašajmo ga kaj bolj zapletenega. Na primer: kdo so Thomasovi otroci?
+</p>
+<pre>
+?- parent(thomas, X).
+X = william ;
+X = sally ;
+X = jeffrey ;
+no.
+</pre>
+<p>
+Aha! Kaj je tisto podpičje na koncu in zakaj je rešitev več? Podpičje v resnici
+pomeni logični „ali“, po vsakem prologovem odgovoru (en odgovor, ena vrstica)
+pa nas (če ve, da je rešitev več) prolog počaka, da mu lahko rečemo, da želimo
+več odgovorov. S podpičjem (ali črno „n“ kot „next“) zahtevamo morebitne
+dodatne rešitve, s pritiskom na tipko „Enter“ pa poizvedbo končamo.
+</p>
+<p>
+Kot vidiš, so rešitve tri. Odgovor „no“ na koncu pa samo pomeni, da po tretji
+rešitvi ni nobene rešitve več.
+</p><p>
+Zakaj so imena ljudi v bazi pisana z malo začetnico in zakaj je tisti <code>X</code> zgoraj
+z veliko? Je to pomembno? Seveda je pomembno! Prolog avtomatsko ve, kakšnega
+tipa je določen stavčni element: v splošnem vse, kar je pisano z veliko
+začetnico, smatra za spremenljivke, vse z malo pa za konstante (tehnično za
+atome, ampak za zdaj se ne vtikajmo v to). V večini drugih jezikov bi ljudi v
+bazi verjetno pisali kot nize, jih obdali z narekovaji ali čim podobnim, v
+prologu pa je to bolj preprosto. Skratka, zato so vsa imena ljudi z malo
+začetnico – ne zaradi sloga, pač pa, ker tako mora biti. Sicer bi bil pomen
+drugačen.
+</p><p>
+No, kaj pa tole vprašanje?
+</p>
+<pre>
+?- parent(X, william).
+X = tina ;
+X = thomas.
+</pre>
+<p>
+Čakaj, čakaj, kaj so tu vhodi in kaj izhodi? Tako je, kar navadi se: večinoma
+so vsi argumenti lahko vhodi in izhodi hkrati. Nič ni strogo definirano. Temu
+bomo pogovorno rekli, da prolog deluje v vse (več) smeri. Vprašali smo seveda,
+kdo je starš od Williama.
+</p><p>
+Privoščimo silahko še več.
+</p>
+<pre>
+?- parent(X, Y).
+X = tina, Y = william ;
+X = thomas, Y = william ;
+…
+</pre>
+<p>
+Kako pa to ve? Seveda, saj je zapisano v bazi. Prolog nam je lepo začel
+naštevati, kdo vse je komu starš. Pametno, pametno. To je navsezadnje tudi
+pomen našega vprašanja.
+</p><p>
+Kaj pa je bila tista vejica med obema spremenljivkama zgoraj? Medtem ko
+podpičje pomeni logični „ali“, vejica pomeni logični „in“. Izkoristimo to novo
+znanje in vprašajmo prolog še nekaj malce bolj zapletenega, kdo vse so
+<em>sinovi</em> od Thomasa? Sinovi so seveda osebe moškega spola, ki jim je
+Thomas starš.
+</p>
+<pre>
+?- parent(thomas, X), male(X).
+X = william ;
+X = jeffrey ;
+no.
+</pre>
+<p>
+Vejico ali podpičje lahko torej uporabimo tudi v vprašanju. Seveda! Če bi
+želeli našteti npr. vse osebe v bazi, tako moške kot ženske, bi zapisali
+naslednje vprašanje:
+</p>
+<pre>
+?- male(X) ; female(X).
+…
+</pre>
+
+<h2>Moj prvi program v prologu</h2>
+<p>
+Ne bo „Hello, world“, ker je prelahek in nesmiselen za logični jezik, kot je
+prolog. Bo pa zato tisto, kar nas je večina spoznala kot svojo prvo besedo v
+življenju – relacija mama. Skratka, <code>mother/2</code> bo naš ciljni
+predikat, definiran takole: <code>mother(X, Y)</code> naj pomeni, da je
+<code>X</code> mama od <code>Y</code>.
+</p><p>
+S tem, kar smo spoznali do sedaj, bi se tega lahko lotili tako, da pogledamo
+osebe v bazi, kje relacija velja in te primere zapišemo, nekako takole:
+</p>
+<pre>
+mother(tina, william).
+mother(sally, andrew).
+mother(sally, melanie).
+mother(vanessa, susan).
+…
+</pre>
+<p>
+S tem pravzaprav ni nič narobe in bi povsem lepo delovalo. Problem je le, da je
+zamudno, zelo zamudno. Seveda v nadaljevanju ne bomo popisovali relacij dejstvo
+za dejstvom, ampak bomo stvari poskusili avtomatizirati oz. definirati.
+Avtomatizacija je navsezadnje bistvena lastnost računalnikov.
+</p><p>
+Spoznali bomo še zadnji, tretji (poleg dejstva in vprašalnega stavka) tip
+stavka: pravilo.
+</p><p>
+Kaj mora torej veljati, da je <code>X</code> mama od <code>Y</code>? Dve
+stvari: da je <code>X</code> starš od <code>Y</code> in da je <code>X</code>
+ženskega spola. S tem zadnjim ločimo mame od očetov. Pa zapišimo to s
+prologovim pravilom:
+</p>
+<pre>
+mother(X, Y) :-
+ parent(X, Y),
+ female(X).
+</pre>
+<p>
+Poglejmo sestavne dele zapisanega. Piko na koncu že poznamo, ravno tako že
+vemo, da vejica pomeni logični „in“. Torej je <code>X</code> starš <em>in</em>
+ženska hkrati.
+Kaj pa pomeni nenavadni operator <code>:-</code>, ki je tako značilen za
+prolog? Ta operator nam definira pravilo, loči pa ga na dva dela: tisto pred :-
+imenujemo glava stavka, tisto za njim pa telo stavka. Bere pa se zelo preprosto
+takole: če logično velja telo stavka, potem iz tega sledi, da velja tudi glava
+stavka.
+</p><p>
+Za naš primer to pomeni naslednje. <em>Če</em> velja, da je <code>X</code> starš
+od <code>Y</code> in <em>hkrati</em> (vejica!) velja, da je <code>X</code>
+ženskega spola, <em>potem</em> velja, da je <code>X</code> mama od
+<code>Y</code>. Pravzaprav je kot pogojni stavek, kajne?
+</p><p>
+Še nekaj zelo pomembnega. Vsakič, ko sprogramiraš kakšno pravilo – in večina
+programov bo preprosto množica enega ali več pravil – ga preberi tako, kot smo
+ga prebrali zgoraj. To je najboljši način razhroščevanja v prologu! Če se ti
+pravilo ob branju ne zdi smiselno, potem skoraj gotovo ni pravilno; kar zaupaj
+svojemu občutku za logiko.
+</p><p>
+Za konec nekaj besed o slogu programiranja. Kot vidiš, sem pisal posamezne
+konjunkte v svojo vrstico, celotno telo stavka pa sem tudi nekoliko zamaknil.
+Vse to ni nujno potrebno, se pa tako veliko lažje bere in posledično lažje
+najde morebitno napako.
+</p><p>
+Tako, sedaj si nared, da kaj tudi samostojno sprogramiraš. Vse relacije
+(naloge) v tem sklopu so definirane tako, da je <code>X</code> v relaciji z
+<code>Y</code> in ne obratno. Tako <code>father(X, Y)</code> pomeni, da je
+<code>X</code> oče od <code>Y</code> in ne, da je <code>Y</code> oče od
+<code>X</code>.
+Naloge rešuj povsem naravno, ne obremenjuj se s tem, da morajo delovati v „vse
+mogoče smeri“, kot je delovala relacija <code>parent/2</code>. Boš videl, da bo
+to večinoma prišlo kar samo od sebe kot bonus. Sem se vrni, ko naletiš na prvo
+rekurzijo, to je relacijo <code>ancestor/2</code>.
+</p>
+<!--
+Tipično smo se prej ustavili že pri brother/sister zaradi potrebe po operatorju
+\==, ampak sedaj to uredijo namigi. Poleg tega za konec (je to implementirano,
+najbrž ni?) tam povemo kot zanimivost kaj bi bilo, če je \== prvi cilj v
+telesu. -->
+
+<h2>Prva rekurzija</h2>
+<p>
+Prolog načeloma ne uporablja standardnih kontrolnih struktur, kot so npr.
+while ali for zanke, zamišljen je povsem drugače. Saj to si do sedaj že opazil.
+Zato je rekurzija glavno orodje „avtomatizacije“ oz. nadomešča zanke.
+</p><p>
+Kako se je lotimo? Načeloma ima vsaka rekurzivna definicija robni primer in
+splošni primer. Obojih je lahko tudi več. Tipično (to je seveda odvisno od
+tvojega sloga) za vsak primer napišeš svoje pravilo.
+</p><p>
+Robni primer tipično predstavlja najbolj enostaven primer, ko relacija velja.
+Spomnimo se matematične indukcije, rekurzija ji je zelo podobna! Tipično
+najprej dokažeš, da relacija velja za n=1 (robni primer), potem pa se lotiš
+težjega, splošnega primera takole: <em>predpostaviš</em>, da relacija velja za
+nek n in poskusiš pokazati, da če je to res, da potem relacija velja tudi za
+n+1. Pri rekurziji v splošnem primeru razmišljaš takole: kako lahko primer
+preveden na enak, le kanček bolj enostaven primer? Najbolje je to videti na
+primeru, definirajmo relacijo prednik, torej <code>ancestor/2</code>.
+</p><p>
+Začnimo takole: vprašajmo se, kdaj je nek <code>X</code> prednik od
+<code>Y</code> na najbolj enostaven način? Predniki od <code>Y</code> so
+njegovi starši, pa dedki in babice, pa pradedki in prababice, pa…. Naštevamo
+(dodajamo predpono pra‐) lahko v nedogled. Ampak najbolj enostaven (najkrajši,
+če relacije gledamo kot družinsko drevo) primer je? Starši, seveda! Robni
+primer je torej:
+</p>
+<pre>
+ancestor(X, Y) :-
+ parent(X, Y).
+</pre>
+<p>
+Spomni se, da se to pravilo prebere kot: če je <code>X</code> starš od
+<code>Y</code>, potem je <code>X</code> prednik od <code>Y</code>. Sliši se
+smiselno, zato gremo naprej.
+</p><p>
+Kako bi se lotili splošnega primera? Kaj, če predpostavimo, da poznamo nekega
+prednika od <code>Y</code>? Recimo mu <code>Z</code>. Potem so tudi
+<code>Z</code>-jevi starši predniki od <code>Y</code>, kajne?
+Znamo to izkoristititi? Seveda!
+</p>
+<pre>
+ancestor(X, Y) :-
+ parent(X, Z),
+ ancestor(Z, Y).
+</pre>
+<p>
+Zanimivo pravilo! Pravi naslednje: če (predpostavka!) je <code>Z</code> prednik
+od <code>Y</code> in hkrati (vejica) je <code>X</code> starš od <code>Z</code>,
+potem je <code>X</code> tudi prednik od <code>Y</code>. Logično, kajne?
+</p><p>
+Pomembno: doseg spremenljivk, kot so zgoraj <code>X</code>, <code>Y</code> in
+<code>Z</code>, je omejen na trenutni stavek!
+Globalnih spremeljivk ni – tehnično to ni čisto res, ampak za zdaj to ni
+pomembo. 😊
+Tako sta <code>X</code> in <code>Y</code> v prvem stavku (robni primer) druga
+kot <code>X</code> in <code>Y</code> v drugem stavku (splošni primer).
+</p><p>
+Vidiš, kako smo relacijo prednik definirali s pomočjo same sebe? To lahko
+storimo, če problem „zmanjšamo“. Če pogledamo družinsko drevo, je
+<code>Z</code> en korak bližji prednik od <code>Y</code>, kot je to
+<code>X</code>.
+Tako je, ko smo postavili zahtevo <code>parent(X, Z)</code>, smo problem
+„zmanjšali“ za en korak. Splošni primer se tako korak za korakom bliža robnemu
+primeru, ki rekurzijo konča.
+</p><p>
+Brez skrbi, s precej vaje, ki jo omogačajo naloge v naslednjih sklopih, ti bo
+rekurzija postala zelo domača. Spoznal jo boš do obisti, obljubim!
+</p>
+
+<h2>Slog programiranja</h2>
+<p>
+Kot si opazil, smo prejšni primer (prednik) rešili z dvema praviloma. Tukaj je
+celotna rešitev:
+</p>
+<pre>
+ancestor(X, Y) :-
+ parent(X, Y).
+ancestor(X, Y) :-
+ parent(X, Z),
+ ancestor(Z, Y).
+</pre>
+<p>
+Rešitev bi lahko zapisali tudi takole:
+</p>
+<pre>
+ancestor(X, Y) :-
+ parent(X, Y)
+ ;
+ parent(X, Z),
+ancestor(Z, Y).
+</pre>
+<p>
+Uporabili smo podpičje, logični „ali“. Preberimo pravilo: <em>če</em> je
+<code>X</code> starš od <code>Y</code> <em>ali</em> če je <code>X</code> starš od
+<code>Z</code> <em>in</em> je hkrati <code>Z</code> prednik od <code>Y</code>,
+<em>potem</em> je <code>X</code> prednik od <code>Y</code>.
+</p><p>
+Tudi ta rešitev je pravilna. Pravzaprav boš kasneje videl, da je povsem enaka,
+le zapisana je drugače. Katero verzijo uporabiš, je bolj ali manj stvar tvojega
+sloga. Kasneje boš opazil, da je včasih ena verzija bolj primerna (krajša),
+včasih pa druga.
+</p><p>
+Še dve pomembni opazki za konec:
+</p>
+<ul>
+ <li>
+Spremenljivke imajo pravzaprav doseg ene veje (konjunkcije). Tako sta v drugem
+primeru <code>X</code> in <code>Y</code> v cilju <code>parent(X, Y)</code>
+druga kot za podpičjem.
+ </li>
+ <li>
+Logični „in“ (vejica) ima prednost pred logičnim „ali“ (podpičje), točno tako,
+kot si vajen iz logike. Če želiš spremeniti, kako so cilji povezani, lahko za
+to seveda uporabiš navadne oklepaje – spet tako kot v logiki.
+ </li>
+</ul>
+
+ </body>
+</html>
diff --git a/prolog/problems/family_relations/sl.py b/prolog/problems/family_relations/sl.py
index 81242ed..a808947 100644
--- a/prolog/problems/family_relations/sl.py
+++ b/prolog/problems/family_relations/sl.py
@@ -1,8 +1,11 @@
name = 'Družinske relacije'
description = '''\
-<p>Prvi koraki v prologu – pisanje pravil za različne družinske relacije.
-Za naloge v tem sklopu je že definirana baza podatkov o
+<p>
+<a target="_blank" href="[%@resource intro_sl.html%]">Prvi koraki v prologu</a>
+– pisanje pravil za različne družinske relacije. Za naloge v tem sklopu je že
+definirana baza podatkov o
<a target="_blank" href="[%@resource famrel.svg%]">družinskih drevesih</a>.
V prologu so te informacije predstavljene s predikati <code>parent/2</code>,
-<code>male/1</code> in <code>female/1</code>.</p>
+<code>male/1</code> in <code>female/1</code>.
+</p>
'''
diff --git a/prolog/sl.py b/prolog/sl.py
index 5a408a7..095d253 100644
--- a/prolog/sl.py
+++ b/prolog/sl.py
@@ -1,7 +1,10 @@
# coding=utf-8
name = 'Prolog'
-description = 'Uvodni tečaj prologa.'
+description = '''\
+Uvodni tečaj prologa.
+<a target="_blank" href="[%@resource intro_sl.html%]">Napotki za uporabo aplikacije.</a>
+'''
hint = {
'no_hint': '''\
diff --git a/prolog/style.css b/prolog/style.css
new file mode 100644
index 0000000..2000a50
--- /dev/null
+++ b/prolog/style.css
@@ -0,0 +1,60 @@
+body {
+ max-width: 60em;
+ margin: 0 auto;
+ padding: 1em;
+ hyphens: auto;
+ -moz-hyphens: auto;
+ -ms-hyphens: auto;
+ -webkit-hyphens: auto;
+}
+
+figure {
+ margin: 0 auto;
+}
+figure > figcaption {
+ text-align: center;
+ width: 100%;
+}
+figure img {
+ display: block;
+ margin: 0.25em auto;
+ max-width: 80%;
+}
+
+li > p {
+ margin: 0;
+}
+li > p:last-child {
+ margin-bottom: 0.5em;
+}
+
+p {
+ text-align: justify;
+}
+
+/* code snippets */
+code, pre {
+ font-size: 0.95em;
+}
+
+code {
+ background-color: #f4f2f9;
+ color: #1525c6;
+ padding: 0.1em 0.2em;
+}
+
+pre {
+ padding: 0.5em 1em;
+ margin: 0 0 10px;
+ color: #333;
+ word-break: break-all;
+ word-wrap: break-word;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
+
+/* CodeQ name in small caps */
+span.codeq {
+ font-variant: small-caps;
+}