V minulé kapitole jsme se naučili rekurzi. Možná jste si říkali: "K čemu je to dobré v praxi?" Tady je odpověď. Rekurze je naprosto nezbytná pro práci se seznamy. Představte si hrdinu v RPG hře. Má batoh (inventář), ve kterém může mít meč, lektvar, mapu, nebo taky nic. Nevíme předem, kolik věcí tam bude. Pro uchování takové řady věcí používáme seznamy.
Seznam v Prologu se píše do hranatých závorek: [jablko, hruska, banan]. Abychom se seznamem mohli pracovat, musíme ho umět rozebrat. Prolog vidí seznam jako vlak, který má dvě části:
Pro rozdělení vlaku na lokomotivu a zbytek používáme "svislítko" | (pipe). Zápis [H | T] znamená: "Vezmi první prvek a dej ho do H, zbytek dej do T."
?- [H | T] = [jablko, hruska, banan].
H = jablko,
T = [hruska, banan].
?- [H | T] = [jablko].
H = jablko,
T = []. % Prázdný seznam!
⚠️ Častá chyba: Ocas seznamu [a] není nic, ale prázdný seznam []. Je to jako prázdný vlak, který už nemá žádné vagóny.
Jak zpracovat celý vlak? Jednoduše:
Hledáme, jestli je v batohu X.
% 1. Základní případ: Našli jsme to! X je hned na začátku (v Hlavě).
obsahuje(X, [X | _]).
% 2. Rekurzivní krok: X na začátku není. Musíme se podívat do zbytku (Ocasu).
obsahuje(X, [_ | Ocas]) :-
obsahuje(X, Ocas).
Jak to funguje: ?- obsahuje(banan, [jablko, hruska, banan]).
banan to samé co jablko? Ne. (Pravidlo 1 selže).jablko a hledáme banan v [hruska, banan].banan to samé co hruska? Ne.hruska a hledáme banan v [banan].banan to samé co banan? ANO! (Pravidlo 1 uspěje).true.Jak dlouhý je vlak?
delka([], 0). % Prázdný seznam má délku 0.
delka([_ | Ocas], N) :- % Délka je 1 + délka ocasu.
delka(Ocas, N1), % Zjisti délku zbytku
N is N1 + 1. % Přičti 1
Příklad 8.1: Součet seznamu
Máme seznam mincí [1, 2, 5, 10]. Kolik máme celkem peněz?
soucet([], 0). % Prázdný měšec má hodnotu 0.
soucet([Hlava | Ocas], Celkem) :-
soucet(Ocas, SoucetZbytku), % Nejdřív sečti zbytek
Celkem is Hlava + SoucetZbytku. % Pak přičti aktuální minci
Většina predikátů pro práci se seznamy má stejnou strukturu:
1. Základní případ pro prázdný seznam [].
2. Rekurzivní pravidlo pro seznam s hlavou a ocasem [H|T], které zpracuje hlavu a zavolá se znovu na ocas.
obsahuje prohodili pořadí pravidel? Fungovalo by to stále?[a]? A jaký je ocas seznamu []? (Chyták: prázdný seznam nemá ocas, pokus o jeho získání selže).[_, _, X | _]).posledni(X, Seznam), který najde poslední prvek seznamu. (Nápověda: Poslední prvek v seznamu [X] je X. Pokud je seznam delší, poslední prvek je v ocasu).spoj(S1, S2, S3), který spojí dva seznamy dohromady. (Tohle je těžší, zkuste si to nakreslit jako spojování dvou vlaků).