---
# System prepended metadata

title: FLP -- Prolog

---

# FLP -- Prolog
## 2024/25
1. Vytvorte predikát `transp(+InMat,-OutMat)`, ktorý transponuje zadanú maticu. Bolo uvedených zopár príkladov použitia.

```prolog
transp( [[H|T]|LS], [[H|TT]|XS] ) :-
    cont(LS,TT,Rests),
    transp2( [T|Rests], XS ).

cont( [], [], [] ).
cont( [[H|T]|LS], [H|TT], [T|TTS] ) :-
    cont( LS, TT, TTS ).

transp2( [[]|_], [] ). % :-
    transp2( [[H|T]|LS], [[H|TT]|XS] ) :-
    cont(LS,TT,Rests),
    transp2( [T|Rests], XS ).
```

2. Definujte predikát `isMatOK(+Mat)`. Predikát skončí úspešne, pokiaľ má vstupná matica v každom riadku a v každom stĺpci práve jednu hodnotu 1, ostatné hodnoty 0.

```prolog
isMatOK(M) :-
    tst(M),
    transp(M,NT),
    tst(NT).
    
tst( [] ).
tst( [L|LS] ) :-
    isLineOK(L),
    tst(LS).
    
isLineOK( [1|R] ) :- null(R).
isLineOK( [0|R] ) :- isLineOK(R).
null( [] ).
null( [0|R] ) :- null(R).
```

3. Vytvorte predikát `fs(+N,-Mat)`, ktorý pre daný rozmer šachovnice `NxN` nájde také šachovnice `M`, kde bude rozmiestnených `N` dám. Dámy musia byť umiestnené tak, aby sa vzájomne neohrozovali. Môžete použiť predikáty s predošlých úloh, kľúčove slovo `is`, základnú konštrukciu zoznamov, `append`, číselné operácie a predikát `isDiagOk(M)`, ktorý skontroluje či sa žiadne 2 dámy neohrozujú po diagonále. 

```prolog
fs(N,M) :-
    prep(1,N,SM),
    varm(SM, M),
    isMatOK(M),
    isDiagOK(M).

gen(0,[]).
gen(N,[0|R]) :-
    N>0, NN is N-1,
    gen(NN,R).

varm( [], [] ).
varm( [L|LS], [NL|NLS] ) :-
    varm(LS,NLS),
    append(S,[_|R],L),
    append(S,[1|R],NL).

prep(N,N,[L]) :- gen(N,L).
prep(X,N,[L|LS]) :- gen(N,L),
    X<N, XX is X+1,
    prep(XX,N,LS).
```



## 2023/2024
### 1. opravný termín

Pozn. na začátku každého úkolu je napsáno: "V holém jazyku Prolog -- tedy jen termy, predikáty, unifikace (nikoliv negace unifikace) -- ..."

1. /2b/ Definujte predikáty `ser(+R1, +R2, -Rser)` a `parR(+R1, +R2, -Rpar)`, které spočtou odpor sériového a paralelního zapojení resistorů -- `Rser = R1 + R2` a `Rpar = 1/((1/R1) + (1/R2))`. Nad holý Prolog použijte operace nad čísly a operátor is.

```prolog
serR(R1,R2,R) :- R is R1+R2.
parR(R1,R2,R) :-
    R is 1/((1/R1)+(1/R2)).
```
2. /5b/ Definujte termy pro složení libovolného sériového/paralelního/sério-paralelního zapojení resistorů libovolné velikosti a složitosti a popište je. S využitím predikátů z předchozího příklad (pokud je nemáte, jako byste měli). Definujte predikát `resistance(+Zapojeni, -Odpor)`, který pro zadané zapojení resistorů spočte jejich odpor. Krom vlastního predikátu `resistance` a těch z předchozího příkladu nic jiného použít nesmíte, tedy až na termy definované v tomtu příkladu pro popis zapojení.

```prolog
% res(Ohm) - resistor
% serial(R1,R2) - seriove zapojeni
% paralel(R1,R2) - paralelni zapojeni

resistance(res(O),O).
resistance(serial(R1,R2),R) :-
    resistance(R1,O1), resistance(R2,O2),
    serR(O1,O2,R).

resistance(paralel(R1,R2),R) :-
    resistance(R1,O1), resistance(R2,O2),
    parR(O1,O2,R).
```

3. /6b/ Definujte predikát `bagdiff(+Bag1, +Bag2, -BagR)`, který pro zadané dvě multimnožiny `+Bag1` a `+Bag2` spočte jejich multimnožinový rozdíl a unifikuje do výstupního parametru `BagR`. Predikát pracuje jako funkce. Multimnožiny jsou reprezentovány seznamem.

```prolog
bagdiff([], _, []).
bagdiff(R, [], R).
bagdiff(Bag1, [Bag2H|Bag2T], Bagr) :-
    member(Bag2H, Bag1),!,
    remove(Bag1, Bag2H, Res),
    bagdiff(Res, Bag2T, Bagr).
bagdiff(Bag1, [_|Bag2T], Bagr) :-
    bagdiff(Bag1, Bag2T, Bagr).
    

remove([],_, []).
remove([H|T], H, T):- !.
remove([H|T], S, [H|Res]):-
    remove(T, S, Res).

member(S, [S|_]).
member(S, [_|Rest]):-
    member(S, Rest).
```

4. /6b/ Definujte predikát `getRes(+BagRes, -ConRes, -RemainingBag)` který pro zadanou multimnožinu `BagRes` (zadanou seznamem) hodnot odporu jednotlivých resistorů postupně unifikuje (přes zpětné navracení) do parametru `ConRes` buďto ty odpory jako samotné, nebo ve všech kombinacích sériového a paralelního zapojení (jen jedna úroveň). K tomu použijte termy (pro ten `ConRes`) pro reprezentaci zapojení z předchozího příkladu. Do parametru `RemainingBag` se unifikuje multimnožina (jako seznam) hodnot odporu neužitých v zapojení `ConRes`.

```prolog
getRes(List,res(Val),NL) :-
    append(_,[Val|_],List),
    remove(List,Val,NL).
getRes(List,serial(res(R1),res(R2)),NL) :-
    append(_,[R1|_],List),
    remove(List,R1,L1),
    append(_,[R2|_],L1),
    remove(L1,R2,NL).
getRes(List,paralel(res(R1),res(R2)),NL) :-
    append(_,[R1|_],List),
    remove(List,R1,L1),
    append(_,[R2|_],L1),
    remove(L1,R2,NL).
```

5. /11b/ Definujte predikát `getAny(+BagRes, +Res, -Connection)`, který pro zadanou multimnožinu odporů (jako seznam) `BagRes` a hodnotu požadovaného odporu `Res` (desetinné či celé kladné číslo) vyhledá z odporů v multimnožine `BagRes` takové zapojení, aby dávalo požadovaný odpor. Nad holý Prolog můžete použít základní práci se seznamy, operátor `is`, operace nad čísly, `assert*`, `retract*`, a predikáty a termy z předchozích příkladů. Pozn: Po nutné úpravě je možné úspešně využít schéma z přednášky. Navíc není nutné, aby dodávané výsledky byly dodávany nějak bez opakování -- viz dodatkový list.

```prolog
getAny(List,Val,Res) :-
    getCon(List,Wal,Res,_),
    Rval is Val*1.0,
    Rwal is Wal*1.0,
    Rwal=Rval.

con(R1,V1,R2,V2,serial(R1,R2),Val) :-
    serR(V1,V2,Val).
con(R1,V1,R2,V2,paralel(R1,R2),Val) :-
    parR(V1,V2,Val).

getCon(List,Val,Res,Rem) :-
    getRes(List,Res,Rem),
    resistance(Res,Val).
getCon(List,_,_,_) :-
    par(List),!,fail.
getCon(List,Val,Res,Rem) :-
    assert(par(List)),
    getCon(List,V1,R1,Rest1),
    getCon(Rest1,V2,R2,Rem),
    con(R1,V1,R2,V2,Res,Val).
getCon(List,_,_,_) :-
    retract(par(List)),!,fail.
```

### Řádný termín

1. Navrhni dátovú štruktúru reprezentujúcu zásobník a na ňom operácie `empty/1, push/3, pop/3, top/2, size/2` tak, aby pracovali v konštantnom čase (aj `size` !). (Pozn. prve 4 úlohy spolu súvisia)

```prolog
% zasobnik: st(pocet polozek, polozky v seznamu)
% st(count,[vals])
empty(st(0,[])).
push(st(C,S),V,st(CC,[V|S])) :- CC is C+1.
pop(st(C,[V|S]),V,st(CC,S)) :- CC is C-1.
top(st(_,[V|_]),V).
size(st(C,_),C).
```
2. Predikát `pp/3`, `pp(?Stack, ?Item, ?Stack)`, ktorý podľa toho, či bude vľavo alebo vpravo daný zásobník, sa bude správať aj ako push (vľavo vstupný zás. a item, vpravo po pushi) aj ako pop (vpravo vstupný zásobník, vľavo popnutný zásobník, v strede popnutý item). (nemožno použiť push a pop z predošlej úlohy, možno použiť `nonvar` a aritmetické operácie).
```prolog
pp(In,V,Res) :-
    nonvar(In),!,In=st(C,S),
    CC is C+1, Res = st(CC,[V|S]).
pp(In,V,st(CC,[V|S])) :-
    C is CC-1, In = st(C,S).
```
3. Predikát `pushStr/3`, `popStr/3`, ktorý dokáže pushnúť viacero prvkov naraz. `popStr` bude zároveň vedieť kontrolovať, či sekvencia, ktorá sa popuje, sedí s tým, čo je na vrchole zásobníka - ak nie, tak failne.
```prolog
pushStr(Stack,[],Stack).
pushStr(Stack,[X|XS],NewStack) :-
    pushStr(Stack,XS,SubStack),
    push(SubStack,X,NewStack).

popStr(Stack,[],Stack).
popStr(Stack,[X|XS],NS) :-
    pop(Stack,X,RetStr),
    popStr(RetStr,XS,NS).
```
4. Predikát `remN/3`, ktorý podľa zadanej hodnoty popne daný počet prvkov zo zásobníka. Ak je zadané číslo menšie rovné nule, nič sa nestane, ak je zadané číslo väčšie ako veľkosť zásobníka, predikát zlyhá.
```prolog
remN(Stack,N,Stack) :- N =< 0.
remN(Stack,N,Res) :-
    N>0,
    size(Stack,C),
    N =< C, NN is N-1,
    pop(Stack,_,NewStack),
    remN(NewStack,NN,Res).
```
5. V predikáte `sum4/3` implementovať SUBSET SUM problém (pre zadaný zoznam čísel a nejakú hodnotu nájdi všetky podmnožiny, ktorých súčet sa rovná danému číslu) - predikát bude vracať všetky výsledky a potom failne. (operátor rezu možno použiť max 1x)

```prolog
% priklad pouziti:
?- sum4([1,2,3,4,5,6], 7, R).
R = [1,2,4] ;
R = [1,6] ;
R = [2,5] ;
R = [3,4] ;
false.
```

```prolog
sum4(_,0,[]) :- !.
sum4([X|XS],N,[X|Rest]) :-
    X =< N,
    NN is N-X,
    sum4(XS,NN,Rest).
sum4([_|XS],N,Res) :-
    sum4(XS,N,Res).
```

## 2022/2023
### Řádný
Implementace funkce prime/1, která ověří, že X je prvočíslo a při zadání prime(X) generuje nekonečkou posloupnost prvočísel.

```prolog
allNums(1, 1) :- !.
allNums(N, X) :- X is N - 1.
allNums(N, X) :- N2 is N - 1, allNums(N2, X).

generateinfity(1).
generateinfity(X) :- generateinfity(X2), X is X2 + 1.
generateinfity(X) :- generateinfity(X).

not_prime(X) :- allNums(X, X1), allNums(X, X2), X is X1 * X2.

prime2(X) :- not_prime(X), !, fail.
prime2(_).

prime(1).
prime(X) :- generateinfity(X), prime2(X).
% does not seem to work properly
```
alebo, funkcnejsie riesenie:

```prolog
generate_naturals(0).
generate_naturals(X) :- generate_naturals(Y), X is Y + 1.

generate_divisor(N, N) :- N > 1.
generate_divisor(N, X) :- N > 1, N1 is N - 1, generate_divisor(N1, X).

primeGen(X) :- generate_naturals(NextNumber), primeCheck(NextNumber), X is NextNumber.

primeCheck(X) :- X > 1, not((generate_divisor(X div 2, Divisor), X mod Divisor =:= 0)).

% X is integer => check if its prime
prime(X) :- integer(X), !, primeCheck(X).

% falling here means X is a variable => generate
prime(X) :- primeGen(X).
```



### Předtermín

Implementace deterministickeho Turingova stroje. Je dano `:- dynamic tol/1 tor/1 state/1 head/1.`, kde:
- `state` je predikat reprezentujici aktualni stav Turingova stroje.
- `head` obsahuje symbol pod hlavou.
- `tol` obsahuje seznam reprezentujici pasku vlevo od hlavy.
- `tor` stejne tak, ale vpravo od hlavy. 

V obou pripadech je prvni prvek seznamu policko pasky nejblize hlave. Semantika TM je klasicka, tj. neni mozne se posunout doleva, pokud seznam `tol` je prazdny. Doprava je mozne se posunout vzdy (blank pod hlavou). Krok TS je reprezentovany termem 
- `move(State, Symbol, Action)`, kde `State` je soucasny stav, `Symbol` symbol pod hlavou a `Action` akce, co se ma provest (viz 4).

V zadani zminoval, ze mame vzdy co nejvic vyuzivat predikaty vzdy z predchozich ukolu.

1) Implementujte predikat `shl/0`, co pokud je to mozne, pak provede posun hlavy doleva a uspeje. (7b) V dalsich prikladech muzeme predpokladat existenci predikatu 
- `shr/0`, co posouva doprava,
- `wh/1`, co zapisuje pod hlavu a
- `rh/1`, co cte symbol pod hlavou.
```prolog
shl :- tol([NewHead|NewLTape]),
        head(OldHead),
        retract(head(_)), assertz(head(NewHead)), 
        retract(tol(_)), assertz(tol(NewLTape)),
        tor(OldRTape),
        retract(tor(_)), assertz(tor([OldHead|OldRTape])).

```
2) Implementujte predikat `ttol/1`, co do prvniho argumentu vrati obsah cele pasky (5b za "oneliner", jinak 3b)
```prolog
% this could be considered a oneliner
ttol(FullTape) :- 
    tol(LTape), 
    tor(RTape), 
    head(Head), 
    reverse(LTape, RevLTape), 
    append(RevLTape, [Head|RTape], FullTape).
```
3) Implementujte predikat `findmove(Moves, Action)`, co ve vstupnim seznamu `Moves` (jeho obsah jsou termy `move`) najde proveditelnou akci a zunifikuje ji do argumentu `Action`. Pak tam byla asi 4radkova poznamka o tom, ze v seznamu `Moves` muzou byt i kroky, ktery jdou provest s jakymkoliv symbolem apod, ale mame brat v potaz jen validni. Zaroven mame umoznit backtracking v pripade, ze tech validnich pohybu je vic (ze to v realu nenastane, ale mame to podporovat) (3b)
```prolog
findmove(Moves, Action) :- 
    member(move(State, Symbol, Action), Moves),
    head(Symbol),  % it would be better to use rh(Symbol) instead.
    state(State).
```
4) Implementujte predikat `action/1`, ktery dostane jako argument akci a provede ji. Akce muze byt nasledujiciho tvaru (5b):
    a) `act(w, Sym, State)` - zapise Sym pod hlavu a presune se do stavu State
    b) `act(r, State)` - posune hlavu doprava a presune se do stavu State
    c) `act(l, State)` - posune hlavu doleva a presune se do stavu State

```prolog
action(act(r, State)) :- 
    shr, 
    retract(state(_)), 
    assertz(state(State)).
    
action(act(l, State)) :- 
    shl, 
    retract(state(_)), 
    assertz(state(State)).
    
action(act(w, Sym, State)) :- 
    retract(head(_)), 
    assertz(head(Sym)), % `wh(Symbol)` would be better
    retract(state(_)), 
    assertz(state(State)).
```
5) Implementujte predikat work(Moves, InitState, Tape, FinalStates), ktery dostane mozne prechody TS `Moves` (viz format `move`), pocatecni stav `InitState`, seznam koncovych stavu `FinalStates` a pocatecni obsah pasky `Tape` a uspeje, pokud pro tohle zadani existuje reseni. Tim, ze je to deterministicky TM, mame uvazovat, ze je mozne provest vzdy maximalne jeden krok z `Moves`, tj. neuvazovat backtracking. (10b)
```prolog
work(Moves, InitState, [InitHead|RestOfTape], FinalStates) :- 
    assertz(state(InitState)), 
    assertz(tol([])),
    assertz(tor(RestOfTape)), 
    assertz(head(InitHead)),
    doMoves(Moves, FinalStates).

isInFinal(Finals) :- state(CurrentState), member(CurrentState, Finals), !.

doMoves(Moves, FinalStates) :- isInFinal(FinalStates), !.
doMoves(Moves, FinalStates) :- 
    findmove(Moves, NextAction), 
    action(NextAction), 
    doMoves(Moves, FinalStates).

```
## 2021/2022
### 1. termín
1) [12b.]
Např.: [[1, 2], [2,3,4], []], [[3,4], [1], [1,2,3,4]]

Vstupem seznam množin,
Sjednocení všech množin tvoří univerzum,
Pro každou vstupní množinu vypsat doplněk vůči tomuto univerzu.
K dispozici holý prolog.
```prolog!
complements(I,O) :-           % since there is only one possible "universe",
    getUniverse(I,U),         % there is only one correct list of differences =>
    getComplements(I,U,O), !. % use cut to not search for other (wrong) answers
 
getComplements([],_,[]). % no input "sets" to produce complements for
getComplements([HI|TI],U,[HO|TO]) :-
    getDifference(HI,U,HO), % complement = universal set \ our set
    getComplements(TI,U,TO).
 
getDifference(U,U,[]).
getDifference(_,[],[]).
getDifference(S,[HU|SU],[HU|R]) :-
    notMember(HU,S),
    getDifference(S,SU,R).
getDifference(S,[_|SU],R) :-
    getDifference(S,SU,R).
 
getUniverse([],[]).
getUniverse([H|T],U) :-
    getUniverse(T,TU),
    append(H,TU,U).
 
notMember(_,[]).
notMember(X,[H|T]) :-
    notMember(X,T), % the order of these two does not matter
    X \= H.
 
member(_,[]) :- false. % member is not used in the solution
member(X,[X|_]).
member(X,[_|T]) :- member(X,T).
 
append([],X,X).
append(X,[],X).
append(L1,L2,R) :-
    appendInner(L1,L2,T),
    removeDuplicates(T,R).
 
appendInner([],X,X).
appendInner(X,[],X).
appendInner([H1|T1],L,[H1|R]) :-
    appendInner(T1,L,R).
 
removeDuplicates([],[]).
removeDuplicates([H|T],[H|R]) :-
    notMember(H,T),
    removeDuplicates(T,R).
removeDuplicates([_|T],R) :- removeDuplicates(T,R).
```

2) [4b.] splt
chová se jako span v Haskellu.
splt (P, A, AT, AF)

vstupem predikát P a seznam A.
Výstupem dva seznamy: 
AT = souvislý prefix A, kde pro všechny prvky platí predikát P.
AF = zbytek seznamu A(od prvního prvku, kde P neplatí, do konce).
K dispozici holý prolog.
```prolog
% Pozn.: call a not nie sú v holom prológu, ale bez toho to nejde implementovať
splt(_, [], _, []).
splt(P, [H|T], N, AF) :- 
    call(P, H),            % zavolanim P(H) zistime, ci predikat plati na H
    N = [H|Rest],          % (ak ano), tak ho pripojime do vysledku
    splt(P, T, Rest, AF).  % a pokracujeme dalej
% prvy vysledok bude prazdny iba ak predikat zlyha hned na hlave
splt(P, [H|T], [], [H|T]) :- not(call(P, H)).

% alebo ine riesenie
splt(P,[HA|TA],[HA|ATA],AF) :-
    call(P, HA), !, % cut, aby vratilo len to jedine "maximalne" riesenie
    splt(P,TA,ATA,AF).
splt(_,L,[],L).
```
3) [asi 6b.]
Vyhledání všech klíčů ve stromě, které se vážou k dané hodnotě.
Klíče ve stromě byly unikátní, hodnoty byly neznámého typu
```prolog
% test příslušnosti do seznamu
elem(H, [H|_]) :- !.
elem(H, [_|T]) :- elem(H, T), !.
% sjednocení množin
union([], R, R) :- !.
union(R, [], R) :- !.
union([H|T], U, R) :- elem(H, U), union(T, U, R), !.
union([H|T], U, [H|R]) :- union(T, U, R), !.

% definice stromu
% strom může být prázdný, to jsem vyjádřil predikátem myTree(stop).
myTree(stop).
% strom může mít tři položky 
% -> dvojici (klíč, hodnota), to v prologu vyjádřím zápisem -(_K, _V),
%     spojovník a závorky vyjadřuji, že je to dvojice,
%     podtržítko na začátku vyjadřuje anonymitu.
% -> levý podstrom Left
% -> pravý podstrom Right
myTree(tree(-(_Key, _Value), Left, Right)) :-
	myTree(Left),
	myTree(Right). % musí nutně platit, že Left i Right jsou stromy.
% hledání klíče dle libovolné hodnoty v prázdném stromě
% vrátí prázdný seznam jako odpověď
getKeysByValue(_, myTree(stop), []) :- !.
% při hledání klíčů dle hodnoty Val naunifikuji do A, B klíče odpovídající
% hodnotě Val z podstromů Left, Right (po řadě),
% sjednotím je do R a přidám k nim Key, pokud plati,
% ze hodnota aktuálního uzlu odpovídá hledané hodnotě
getKeysByValue(Val, myTree(tree(-(Key, Value), Left, Right)), [Key|R]) :-
	Val == Value,
	getKeysByValue(Val, myTree(Left), A),
	getKeysByValue(Val, myTree(Right), B),
	union(A, B, R),
	!.
% tu predtym chybalo, co sa ma stat, ak Val != Value => Key sa neprida do vysledku
getKeysByValue(_, myTree(tree(-(_Key, _Value), Left, Right)), R) :-
	getKeysByValue(Val, myTree(Left), A),
	getKeysByValue(Val, myTree(Right), B),
	union(A, B, R),
	!.

% Zkuste si třeba:
myTree(stop).
myTree(tree(-(1, 2), stop, tree(-(2, 3), stop, stop))).
getKeysByValue(2, myTree(tree(-(10,2), tree(-(1, 2), stop, stop), tree(-(100, 0), stop, tree(-(1000, 2), stop, stop)))), R).

% --Nebo-------
% To predtim mi uplne nefungovalo (uz je to fixed). Toto mi jde uz spustit:
% Dole jsou i "testy"

union([], [], []).
union([], T, T).
union([H|T], S, [H|U]) :-
    \+ member(H, S),
    union(T, S, U).
union([H|T], S, U) :-
    member(H, S),
    union(T, S, U).

myTree(stop).

myTree(tree(-(_Key, _Value), Left, Right)) :-
    myTree(Left),
    myTree(Right).

getKeysByValue(_, stop, []) :- !.
getKeysByValue(Val, tree(-(Key, Value), Left, Right), R) :-
    getKeysByValue(Val, Left, A),
    getKeysByValue(Val, Right, B),
    (Val == Value -> union(A, B, AB), union(AB, [Key], R) ; union(A, B, R)),
    !.
    
main :-
  union([1,2,3], [2,3,4], U1),
  format('Test 1: Union = ~w~n', [U1]),

  myTree(tree(-(1, a), tree(-(2, b), stop, stop), tree(-(3, c), stop, stop))),
  format('Test 2: myTree passed~n'),

  getKeysByValue(a, tree(-(1, a), tree(-(2, b), stop, stop), tree(-(3, a), stop, stop)), K1),
  format('Test 3: Keys = ~w~n', [K1]),

  halt.


```

4) [asi 8b.]
~jsme v nekonečném stavovém prostoru, prakticky všechno je neznámé nebo dané parametrem a úkolem je udělat krok/cestu v rámci stavového prostoru ... no idea

### 1. opravný
1. vytvoriť funkciu `notelem(+Val,+List)`, ktorá failne ak sa polozka nachádza v zadanom liste. - moze sa poouzit `!, fail`
```prolog
notelem(_,[]).
notelem(X,[X|_]) :- !, fail.
notelem(X,[_|XS]) :- notelem(X,XS).
```
2. implementovat `keyval(Key, Val)` (cca 15b)
- pouzite assert, nonvar, var, !
- ak sú zadané key aj value skúsi vložiť do pamati 
```prolog
keyval(1, jeden)
keyval(2, dva)
keyval(3, tri)
```
- ak sú zadany len key, nájde value z pamäti
```prolog
keyval(1, V)
V = jeden

keyval(5, V)
false.
```
- len value - nájde keys
```prolog
keyval(K, jeden) 
K = [1,11]
```
- keyval(K, V) - vypíše z pamati
```prolog
K = 1
V = jeden

K = 2
V = dva
```
```prolog
:-dynamic
    kvpair/2.

keyval(Key,Val) :-
    var(Key), var(Val), !, kvpair(Key,Val).
keyval(Key,Val) :-
    var(Val), !, kvpair(Key,Val).
keyval(Key,Val) :-
    var(Key), !, collect(Key,Val).
keyval(Key,Val) :-
    testOrInsert(Key,Val).

collect(Key,Val) :-
    kvpair(K,Val), !,
    take([K],Val,Key).
    
take(Keys,Val,Res) :-
    kvpair(K,Val), notelem(K,Keys), !,
    take([K|Keys],Val,Res).
take(Keys,_,Keys).

testOrInsert(Key,Val) :-
    kvpair(Key,V), !, Val=V.
testOrInsert(Key,Val) :-
    assertz(kvpair(Key,Val)).
```

3. `remKey(Key)` - odstráni Key z pamati, neuspeje ak nenájde, `remAll` - uspeje vždy raz, odstráni predikáty z pamäti
```prolog
remKey(Key) :-
    kvpair(Key,Val),
    retract(kvpair(Key,Val)).
remAll :-
    kvpair(Key,Val), !,
    retract(kvpair(Key,Val)),
    remAll.
remAll.
```

4. vytvoriť zjednocovanie zanorenych listov do obycajného listu - zostanú len najvonkajšejšie zátvorky (najľavejšia+najpravejšia)
`destr([[1,[]],[2,[3],4],[[], 5]],[1,2,3,4,5])`
[tu je aj alternativne riesenie z 2017/18](https://hackmd.io/C7K8Nvc5R4uO9I0htzkC1A#%C5%98%C3%A1dn%C3%BD-term%C3%ADn2)
```prolog
destr([],[]).
destr([[]|X],Y) :- % if we encounter an empty list, we just skip it
    destr(X,Y),!.  % and continue with the rest
destr([[H|T]|X],Y) :- % if we encounter a list, we process its head
    destr([H|[T|X]],Y),!. % and merge the tail with the rest of the input
destr([H|T],[H|TT]) :- % now we just process elements within one list
    destr(T,TT).
```
### Riadny
Lambda calcul v prologu
- viz [riadny 19/20](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?view#riadny)
```prolog
/* 1
var(V)       variable     abs(f,abs(x,var(x))) ... 0
app(E1,E2)   application  abs(f,abs(x,app(var(f),var(x)))) ... 1
abs(V,E)     abstraction  abs(f,abs(x,app(var(f),app(var(f),var(x)))))
V - variable
E,E1,E2 - lambda-expression
*/
```
2 - z toho riesenia mi pride, ze uloha bola najst vsetky premenne v danom lambda vyraze
```prolog!
% find all "free" variables in a lambda expression
fv(var(V),[V]).
fv(app(E1,E2),Res) :-
    fv(E1,R1), fv(E2,R2),
    union(R1,R2,Res).
% \x.expr -> x is bound in expr
% so we need to remove it from the result that is propagated up
fv(abs(V,E),Res) :- 
    fv(E,FV),
    del(FV,V,Res).
    
union([],L,L) :- !.
union(L,[],L) :- !.
union([X|XS],YS,Res) :-
    member(X,YS), !,
    union(XS,YS,Res).
union([X|XS],YS,[X|Res]) :-
    union(XS,YS,Res).
    
% delete the first occurence of a value from a list
del([],_,[]).
del([X|XS],X,XS) :- !.
del([X|XS],V,[X|Res]) :-
    del(XS,V,Res).

/* 3 */
valid(Where,What,Var) :-
    fv(What,FV),
    chk(Where,Var,[],FV).
chk(var(V),V,BV,FV) :-
    intersect(BV,FV,I), I==[].
chk(app(E1,E2),V,BV,FV) :-
    chk(E1,V,BV,FV),
    chk(E2,V,BV,FV).
chk(abs(V,_),V,_,_) :- !.
chk(abs(W,E),V,BV,FV) :-
    chk(E,V,[W|BV],FV).
    
intersect([],_,[]) :- !.
intersect(_,[],[]) :- !.
intersect([X|XS],YS,[X|RS]) :-
    member(X,YS),!,
    intersect(XS,YS,RS).
intersect([_|XS],YS,RS) :-
    intersect(XS,YS,RS).
    
/* 4 */
isEta( abs( V, app(E,var(V)) ) ) :-
    fv(E,FV),
    not(member(V,FV)).
isEta(app(E1,E2)) :-
    isEta(E1) ; isEta(E2).
isEta(abs(_,E)) :-
    isEta(E).
```

<!-- ### 1. opravný -->
<!-- ### 2. opravný -->

## 2020/2021
### Řádný termín
Magicke štvorce - NxN matica s hodnotami 1,...,N^2 hodnotami
- info: 1x1 je jednoduche spravit; 2x2 nejde; 3x3 ...
- suma v riadku, stlpci a uhlopriecke je 15 (pri 3x3)
- viz http://www.mathematische-basteleien.de/magsquare.htm
8....1....6
3....5....7
4....9....2


1. spraviť reprezentaciu matice NxN `cms(+N, -M)` 
- `cms(N,M)` skontroluje, že N je číslo, M je pre navrat konečnej matice Magic sqaure
- `cel(N,R)` vytvorí list riadku
- `car(N,N,M)` vytvorí výslednú maticu - list listov (list riadkov pomocou `cel`)
```Prolog
cms(N,M) :-
    integer(N),
    car(N,N,M).
    
cel(N,[_|T]) :-
    N>0,
    NN is N-1,
    cel(NN,T).
cel(N,[]) :-
    N =< 0.
    
car(N,A,[L|T]) :-
    A>0,
    cel(N,L),
    AA is A-1,
    car(N,AA,T).
car(_,A,[]) :-
    A =< 0.
```
2. na poziciu X, Y v matici doplnit hodnotu V a vrátiť výslednú maticu - `set(X,Y,M,V,MM)`

```Prolog
set(X,Y,[H|T],V,[H|TT]) :-
    X > 1, XX is X-1,
    set(XX,Y,T,V,TT).
set(X,Y,[H|T],V,[HH|T]) :-
    X == 1,
    yset(Y,H,V,HH).
    
yset(Y,[H|T],V,[H|TT]) :-
    Y > 1, YY is Y-1,
    yset(YY,T,V,TT).
yset(Y,[_|T],V,[V|T]) :-
    Y == 1.
```
3. doplniť zvyšnú maticu (pomocne funkcie - f. co ti da nevyuzite hodnoty, ...)
```Prolog
solve(N,M,M) :-
    getFree(M,[]),!,
    chk(N,M).
solve(N,M,Res) :-
    getFree(M,Free),
    concat(M,L),
    assertz(size_of(N)),
    step(Free,[],L,[],0,ResL),
    toMS(N,ResL,Res).
solve(_,_,_) :-
    retractall(size_of(_)),
    !,fail.
    
step([],[],[],Rev,_,Res) :-
    reverse(Rev,Res),
    size_of(N),
    toMS(N,Res,M),
    chk(N,M).
step(Free,[],[X|XS],Rev,Ctr,Res) :-
    integer(X),
    CC is Ctr+1,
    try(Free,[],XS,[X|Rev],CC,Res).
step([F|FS],WasFree,[X|XS],Rev,Ctr,Res) :-
    var(X), % Try F
    CC is Ctr+1,
    append(FS,WasFree,Xfree),
    try(Xfree,[],XS,[F|Rev],CC,Res).
step([F|FS],WasFree,[X|XS],Rev,Ctr,Res) :-
    var(X), % F failed
    step(FS,[F|WasFree],[X|XS],Rev,Ctr,Res).
/* rozsireni prubezny test */
try(Xfree,[],XS,Rev,Ctr,Res) :-
    size_of(N),
    D is Ctr div N,
    M is Ctr mod N,
    D > 1,
    M == 0,!,
    reverse(Rev,O),
    check(N,D,O), /* prubezny test */
    step(Xfree,[],XS,Rev,Ctr,Res).
try(Xfree,[],XS,Rev,Ctr,Res) :-
    step(Xfree,[],XS,Rev,Ctr,Res).
/* .............. */
/* rozsireni o prubezny test - test */
sumupto(N,Rest,0,Rest) :- N =< 0.
sumupto(N,[X|XS],S,Rest) :-
	N > 0,
	N1 is N-1,
	sumupto(N1,XS,Sub,Rest),
	S is X+Sub.
check(N,Rows,L) :-
	Rows>1,
	sumupto(N,L,Val,Rest),
	RR is Rows-1,
	chcl(N,RR,Val,Rest).
chcl(_,Rows,_,_) :- Rows == 0.
chcl(N,Rows,Val,L) :-
	Rows > 0,
	sumupto(N,L,Val,Rest),
	RR is Rows-1,
	chcl(N,RR,Val,Rest).
/* -------------- */
/* -------------- */
/* -------------- */
/* co bylo hotove a k uziti, aby to byly komplet, tak zde */
toMS(_,[],[]).
toMS(N,L,[HH|Rest]) :-
	toArray(N,L,HH,TT),
	toMS(N,TT,Rest).

toArray(0,L,[],L).
toArray(N,[H|T],[H|TT],Rest) :-
	N > 0,
	N1 is N-1,
	toArray(N1,T,TT,Rest).
/* .............. */
getFree(MS,ListN) :-
	getUsed(MS,Used),
	length(MS,N),
	genAll(N,All),
	sd(All,Used,ListN).
genAll(N,L) :-
    NN is N*N,
    mkl(1,NN,L).
    
mkl(N,M,[N|T]) :-
	N < M, NN is N+1,
	mkl(NN,M,T).
mkl(N,M,[M]) :-
	N == M.
    
getUsed([],[]).    
getUsed([[]|T],R) :-
    getUsed(T,R).
getUsed([[H|T]|TT],R) :-
	var(H),!,
	getUsed([T|TT],R).
getUsed([[H|T]|TT],[H|R]) :-
    getUsed([T|TT],R).
sd([],_,[]).
sd([X|XS],S,R) :-
	member(X,S),!,
	sd(XS,S,R).
sd([X|XS],S,[X|R]) :-
    sd(XS,S,R).
/* .............. */
concat(L1,L2) :- append(L1,L2).
/* .............. */
chk(N,M) :-
	integer(N),
	N>0,
	length(M,N),
	maplist(length,M,LN),
	all(N,LN),
	append(M,AllVals),
	genAll(N,AllPos),
	vals(AllPos,AllVals),
	csum(M).

all(_,[]).
all(N,[N|T]) :-
    all(N,T).

vals([],[]).
vals(Pos,[X|XS]) :-
	integer(X),
	member(X,Pos),
	delete(Pos,X,NewPos),
	vals(NewPos,XS).
    
csum(M) :-
	maplist(sum_list,M,[H|T]),
	transp(M,MT),
	maplist(sum_list,MT,TT),
	all(H,T), all(H,TT),
	cdiag(H,0,M),
	maplist(reverse,M,MR),
	cdiag(H,0,MR).
cdiag(H,S,[[V]]) :-
	SS is S+V,
	H == SS.
cdiag(H,S,[[V|_]|T]) :-
	SS is S+V,
	maplist(tail,T,TT),
	cdiag(H,SS,TT).

tail([_|T],T).
head([H|_],H).

transp([],[]).
transp([[]|_],[]).
transp(XSS,[HHS|RT]) :-
	maplist(head,XSS,HHS),
	maplist(tail,XSS,TSS),
	transp(TSS,RT).
```
- BONUS: definovat datovy typ obojsmerne viazany zoznam + funkciu ktora zisti dlzku zoznamu - prvky v zozname sa neopakuju

### 1. opravný
1. `genV(+Start,+End, -V)` vygenerovat hodnoty od Start do End vratane (ocakavany vstup je zadany dobre); Start, End su kladné čisla
```prolog
genV(1,3,V).
V=1
V=2
V=3
```

```prolog
genL(E,E,[E]).
genL(S,E,[S|T]) :-
	S<E,
	SS is S+1,
	genL(SS,E,T).
    
genV(S,E,V) :-
	genL(S,E,L),
	append(_,[V|_],L).
    
% alebo ina verzia:
genV(S,E,S):- S < (E + 1).
genV(S, E, V):- S < (E + 1), NS is S + 1, genV(NS, E, V).

% alebo este trochu ina verzia
genV(S, E, S) :- S =< E.
genV(S, E, V) :- S < E, NS is S + 1, genV(NS, E, V).
```
2. `solve(+Xm, +Ym, -X, -Y, -Z)` vyriesit prehladavanim pre `1, ...Xm` a `1...Ym` rovnicu `x²+y²=z²`
```prolog
solve(5,5,X,Y,Z)
x=3
y=4
z=5

x=4
y=3
z=5
atd...
```
```prolog
solve(Xm,Ym,X,Y,Z) :-
	genV(1,Xm,X),
	genV(1,Ym,Y),
	ZZ is X*X+Y*Y,
	genZ(X,Y,ZZ,Z).
    
genZ(M1,M2,ZZ,Z) :-
	max(M1,M2,S),
	Zm is 2*S+1,
	genV(S,Zm,Z),
	Z2 is Z*Z,
	Z2==ZZ.
    
max(X,Y,X) :- X >= Y.
max(X,Y,Y) :- X < Y.

% alebo menej komplikovane
solve(Xm, Ym, X, Y, Z) :-
    genV(1, Xm, X),
    genV(1, Ym, Y),
    L is X * X + Y * Y,
    genV(1, L, Z),
    P is Z * Z,
    L == P.
```
3. `resolve` ako `solve` ale zadava da lava a prava strana rovnice
```prolog
resolve(5,5,X*X*X+Y*Y*Y,Z*Z,X,Y,Z)
X = 1,
Y = 2,
Z = 3

X = 2,
Y = 1,
Z = 3

X = 2,
Y = 2,
Z = 4
```
```prolog
resolve(Xm,Ym,Xe,Ze,X,Y,Z) :-
	genV(1,Xm,X),
	genV(1,Ym,Y),
	ZZ is Xe,
	genF(ZZ,Ze,Z).
    
genF(ZZ,Ze,Z) :-
	Zm is ZZ+1,
	genV(1,Zm,Z),
	Z2 is Ze,
	test(Z2,ZZ).     % skor tu malo byť Z2 = ZZ
    
% alebo menej komplikovane
resolve(Xm, Ym, Le, Pe, X, Y, Z) :-
    genV(1, Xm, X),
    genV(1, Ym, Y),
    L is Le,
    genV(1, L, Z),
    P is Pe,
    L == P.
```
4. `zip(L1,L2,L12)`
```prolog
zip([1,2,3,4],[a,b,c],R)
R = [(1,a),(2,b),(3,c)]
```
```prolog
zip([],_,[]):-!.
zip(_,[],[]):-!.
zip([X|XS],[Y|YS],[(X,Y)|T]) :- zip(XS,YS,T).
```

### 2. opravný termín
Definované:
```prolog
:- dynamic p/1.
node(t,c,92).
node(t,d,45).
node(t,g,71).
node(t,h,40).
node(t,p,67).
node(d,c,50).
node(d,g,42).
node(d,h,20).
node(d,p,54).
node(c,g,36).
node(c,h,54).
node(c,p,58).
node(g,h,32).
node(g,p,22).
node(h,p,36).
```
/8b/ Holý prolog + `not`, `!`, `member`, `length`. Napísať predikát `getAllNodes(+Num)`, ktorý do jediného parametru unifikuje počet jedinečných uzlov. 
```prolog!
% cut prevents unnecessary backtracking and ensures that once a node has been
% added to the list of nodes (L), it is not re-added in subsequent iterations
getNodes(L,Res) :-
    node(N,_,_),
    not(member(N,L)),!,
    getNodes([N|L],Res).
getNodes(L,Res) :-
    node(_,N,_),
    not(member(N,L)),!,
    getNodes([N|L],Res).
getNodes(L,L). % we fall here when all node/3 are explored -> unify working L 

getAllNodes(Len) :-
    getNodes([],L),
    length(L,Len).
```
/22b/ Travelling salesman problem. Napísať predikát `tsp`, ktorý nájde ~~najlacnejšiu trasu medzi dvoma uzlami.~~ najlacnejsiu trasu z daneho uzla cez vsetky uzly naspat do daneho uzla (a kazdy uzol okrem startu navstivime 1x).
```prolog
getlen(X,Y,L) :- node(X,Y,L); node(Y,X,L).

tsp(From,How,Price) :-
    setof(J,solve(From,J),LL), % get all paths starting from our node From
    best(LL,How,Price).        % find the one with the best Price

% `j` functor usage is not necessary, it just improves readability
% solution is a tuple of list (describing the circular path) and price
best([j(H,P)],H,P) :- !.   % there is only one solution, so we save it
best([j(_,P)|R],RH,RP) :-  % there are multiple solutions
    best(R,RH,RP),         % so we find the best solution from the tail
    RP<P, !.               % and conclude that the head solution is worse
best([j(H,P)|_],H,P).      % if it's not worse, then we save it

solve(From,j(How,Price)) :-
    getAllNodes(LAll),    % note that From node will be visited twice
    getlen(From,Nxt,L),   % get some neighbor node (+ its distance)
    go(Nxt,From,Way,P),   % 'go' is basically BFS => gets the solution's tail
    length(Way,LAll),     % make sure that we visited all nodes in the found path
    Price is P+L,         % but the solution needs to add the initial edge price
    How = [From|Way].     % and the starting node

% this is basically BFS
go(T,T,[T],0) :- !.
go(F,T,[F|R],PP) :-
    assertz(p(F)),  % we make sure we visited F,
    getlen(F,N,P),  % so that when we find the neighboring node,
    not(p(N)),      % we can avoid cycles
    go(N,T,R,PR),   % and keep going,
    PP is P+PR.     % while keeping track of the cost of the path
go(F,_,_,_) :-      
    p(F),           % if we reach a dead-end,
    retract(p(F)),  % we 'unvisit' the node
    !, fail.        % backtrack the whole branch until the latest decision point
```

## 2019/2020
### Predtermin
Klika - viz [popis](https://fituska.eu/download/file.php?id=13594)

1 Dána reprezentace pro neorientovaný graf na vstupu ug(vertices,edges),
uložit jako `node(uzel,-1)` a `edge(uzel,uzel)`
```prolog
:- dynamic edge/2, node/2.
insUG(ug(VS,ES)) :-
    inV(VS), inE(ES).
    
inV([]).
inV([V|VS]) :-
    assertz(node(V,-1)), inV(VS).

inE([]).
inE([p(V1,V2)|ES]) :-
    assertz(edge(V1,V2)), inE(ES).
```
2 Otestovat, zda zadaná množina uzlů je klika - graf je v DB
```prolog
isC([_]) :- !.
isC([N|NS]) :-
    checkE(N,NS),isC(NS).
isC([]).

checkE(_,[]).
checkE(N,[X|XS]) :-
    (edge(N,X);edge(X,N)),!,
    checkE(N,XS).
```
3 Doplnit stupeň uzlu ne každému, kde to ještě není nastaveno
```prolog
deg(Node,Deg) :-
    findall(N, edge(Node,N), L1),
    findall(N, edge(N,Node), L2),
    length(L1,N1), length(L2, N2),
    Deg is N1+N2.
    
mkDegs :-
    node(V,-1),!,
    deg(V,D),
    retract(node(V,-1)),
    assertz(node(V,D)),
    mkDegs.
mkDegs.
```
4 Zkusit pro uzel N a jeho okolí Nbr najít mezi nimi co kliku velikosti D-1.
Sousedů je dostatek, nebo více, na to je spoleh.
K dipozici je take jako z Haskellu a perm pro permutace.
```prolog
tryC(D,N,Nbrs, [N|Nbrs]) :-
    length(Nbrs,Len),
    Len == D, !,
    isC([N|Nbrs]).
tryC(D,N,Nbrs, [N|L]) :-
    perm(Nbrs,NX),
    take(D,NX,L),
    isC([N|L]).

/* 5 */
/* Zkusit najít kliku v rozsahu (CD,D>, kde je hlavní uzel N a jeho okolí Nbrs.
Klika bude Clique, když se najde. */
repTry(CD,D,N,Nbrs,Clique) :-
    D > CD,
    tryC(D,N,Nbrs,Clique),!.
repTry(CD,D,N,Nbrs,Clique) :-
    DD is D-1,
    DD > CD,
    repTry(CD,DD,N,Nbrs,Clique).
```
5 Udělat predikát, co zkusít najít větší kliku než zadanou, ideálně tu největší.
Dostane aktuální velikost kliky - 1 (`ActD`), tu kliku a nějakou vrátí (`ResClique`).
K dispozici má `getNbr`, co vrací sousedy pro uzel a `fltr`, co zjistí, jestli to má
k aktuální klice smysl v daném okolí kliku hledat a když ano, tak vrátí to okolí,
kde se to ověří.
```prolog
findC(ActD,_,ResClique) :-
    node(N,D), D>ActD,
    getNbr(N,AllNbrs),
    fltr(D,AllNbrs,NewD,NewNbrs), NewD>ActD,
    repTry(ActD,NewD,N,NewNbrs,Clique),
    length(Clique,LenC),
    DC is LenC - 1, DC > ActD, !,
    findC(DC,Clique,ResClique).
findC(_,C,C).

/* A když to vyjde, bude toto fungovat. */
getClique(ug(VS,ES),Clique) :-
    retractall(node(_,_)),
    retractall(edge(_,_)),
    insUG(ug(VS,ES)),
    mkDegs,
    node(N,_),
    findC(0,[N],Clique),!.
```
### riadny
Lambda Kalkul v prologu:
- viz [riadny 21/22](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both#Riadny2)
```prolog
/* 1 definicie + ako reprezentovat LE v prologu
lambda expression:
variable - var(atom)
lambda-application - app(E1,E2)
lambda-abstraction - abs(atom,E)
where E, E1, E2 are lambda expressions
atom is a name of variable
*/
/* 2 */
fv(E,Vs) :-
    fv3(E,[],Vs).
    
fv3(var(V),Bs,[]) :-
    member(V,Bs), !.
    fv3(var(V),_,[V]).
fv3(app(E1,E2),Bs,Vs) :-
    fv3(E1,Bs,V1),
    fv3(E2,Bs,V2),
    uni(V1,V2,Vs).
fv3(abs(V,E),Bs,Vs) :-
    fv3(E,[V|Bs],Vs).

uni([],L, L).
uni([X|XS],L, YS) :-
    member(X,L), !,
    uni(XS,L, YS).
uni([X|XS],L, [X|YS]) :-
    uni(XS,L, YS).

/* 3 */
subst(Where,What,Var,Res) :-
    fv(What,FW),
    sb(Where,What,FW,Var,[],Res).
    
sb(var(X),_,_,Var,_,var(X)) :- X \= Var, !.
sb(var(X),_,_,X,Bs,var(X)) :- member(X,Bs).
sb(var(X),What,FW,X,Bs,What) :- notIn(FW,Bs), !.
sb(app(E1,E2),What,FW,Var,Bs,app(R1,R2)) :-
    sb(E1,What,FW,Var,Bs,R1),
    sb(E2,What,FW,Var,Bs,R2).
sb(abs(V,E),What,FW,Var,Bs,abs(V,Res)) :-
    sb(E,What,FW,Var,[V|Bs],Res).

notIn([],_) :- !.
notIn(_,[]) :- !.
notIn([X|_],YS) :-
    member(X,YS),
    !, fail.
notIn([_|XS],YS) :-
    notIn(XS,YS).

/* 4 */
doBR(app(abs(Var,Where),What), Res) :- subst(Where, What, Var, Res).
doBR(app(E1,E2), app(Res,E2)) :- doBR(E1,Res).
doBR(app(E1,E2), app(E1,Res)) :- doBR(E2,Res).
doBR(abs(V,E), abs(V,Res)) :- doBR(E,Res).

/* 5 */
findR(S,S,_,[S]) :- !.
findR(_,_,N,_) :-
    N > 5, !, fail.

findR(S,E,N,[S|Rest]) :-
    assertz(la(S)),
    doBR(S,NS),
    not(la(NS)),
    NN is N+1,
    findR(NS,E,NN,Rest).
findR(S,_,_,_) :-
    la(S),
    retract(la(S)),
    !, fail.
```
### 1. opravný
1. 
```prolog
% kontrola ze list Y obsahuje prefix list X
isPref([], _) :-!.
isPref([X|XS],[X|YS]) :- isPref(XS,YS).

% dropuje list L, kym list X obsahuje elementy 
dropL([],L,L) :- !.
dropL([_|XS],[_|YS],R) :- dropL(XS,YS,R).
```
2. 
```prolog
replaceAll(Where,What,By,Res) :-
    length(Where,WL),
    length(What,AL),
    AL =< WL, !,
    doRepl(Where,What,AL,By,Res).
replaceAll(Where,_,_,Where).

doRepl([W|WS], What, AL, By, [R1|Res]) :-
    isPref(What, [W|WS]), !,
    dropL(What, [W|WS], Rest),
    append(By, Rest, R1),
    doRepl(WS, What, AL, By, SR),
    prep(W, SR, Res).
doRepl(WS, _, AL, _, []) :-
    length(WS, WL),
    WL < AL, !.
doRepl([W|WS], What, AL, By, Res) :-
    doRepl(WS, What, AL, By, SR),
    prep(W, SR, Res).

prep(X, XS, [X|XS]).
```
3. 
```prolog
replLines([], _,_, []).
replLines([L|LS], What,By, [RL|RLS]) :-
    replaceAll(L,What,By, RL),
    replLines(LS, What,By, RLS).
```
4. 
```prolog
mkLines([[]|LS],RS) :- !, mkLines(LS,RS).
mkLines([LX|_],_) :- length(LX,L), L>1, !, fail.
    mkLines([[L]|LS],[L|RS]) :- mkLines(LS,RS).
    mkLines([],[]).
```
5. 
```prolog
find(Lines1,SR,Lines2,Res) :-
    assertz(done(Lines1,[])),
    findP(Lines1,SR,Lines2,Res).
find(Lines1,_,_,_) :-
    retract(done(Lines1,[])),
    !,fail
    
findP(Lines1,_,Lines1,[]) :- !.
findP(Lines1,SR,Lines2,[T|TS]) :-
    length(Lines2,LL2),
    append(_,[T|_],SR),
    not(done(_,T)),
    T =.. [sr,What,By],
    replLines(Lines1,What,By,LRep),
    mkLines(LRep,LinesNew),
    length(LinesNew,LN),
    LN >= LL2,
    not(done(LinesNew,_)),
    asr_ret(LinesNew,T),
    findP(LinesNew,SR,Lines2,TS).
    
asr_ret(L,T) :-
    assertz(done(L,T)).
asr_ret(L,T) :-
    retract(done(L,T)),
    !,fail.

```
## 2018/2019
### Řádný termín

Sudoku zadani
```prolog  
/* co bylo k dispozici jako hotove, pro uplnost */
newRC(R,C,R,CC) :-
    C<9, !, CC is C+1.
newRC(R,9,RR,1) :-
    R<9, RR is R+1.
    
getIth(1,[X|_],X) :- !.
getIth(N,[_|XS],X) :- N1 is N-1, getIth(N1,XS,X).

getRC(R,C,M,V) :-
    getIth(R,M,L),
    getIth(C,L,V).
```
1. Napsat predikaty pro zjisteni, jestli je prvek v seznamu a pro odstraneni hodnoty ze seznamu. Dat si bacha na to, ze seznam muze obsahovat nenavazane promenne a prvek se s nima nesmi zunifikovat.
```prolog!
elemNV(X,[V|_]) :- nonvar(V), V=X, !.
elemNV(X,[_|VS]) :- elemNV(X,VS).

remVals([],_,[]).
remVals([X|XS],L,R) :- elemNV(X,L), !, remVals(XS,L,R).
remVals([X|XS],L,[X|R]) :- remVals(XS,L,R).
```
2. Transpozice matice.
```prolog!
trp([[]|_],[]) :- !.
trp(XSS,[L|LS]) :- heads(XSS,L,YSS), trp(YSS,LS).

heads([[H|TS]|XSS],[H|T],[TS|YSS]) :- heads(XSS,T,YSS).
heads([],[],[]).
```
3. Predikat, ktery bere sudoku reprezentovane matici `9x9` a vytvori seznam bloku `3x3`.
```prolog!
blocks([[A,B,C|CS],[D,E,F|FS],[G,H,I|IS]|XSS],[[A,B,C,D,E,F,G,H,I]|LS]) :-
    blocks([CS,FS,IS|XSS],LS).
blocks([[],[],[]|XSS],LS) :- blocks(XSS,LS).
blocks([],[]).
```
4. Predikat, ktery pro danou pozici v sudoku zjisti seznam moznych hodnot, ktere se na to policko daji doplnit. Mame k dispozici predikat `getIth(X,M,L)`, ktery z matice `M` vytahne radek `X` do seznamu `L`. A meli jsme napsane, jak se z pozice `[X,Y]` v matici urci cislo bloku.

```prolog!
valsFor(X,Y,M,[V|VS]) :-
    getIth(X,M,L),
    remVals([1,2,3,4,5,6,7,8,9],L,[H|T]),
    trp(M,TM),
    getIth(Y,TM,R),
    remVals([H|T],R,[HH|TT]),
    blocks(M,BM),
    P is (3*((X-1)//3)) + (1+((Y-1)//3)),
    getIth(P,BM,B),
    remVals([HH|TT],B,[V|VS]).
```
5. Predikat `solves`, ktery s vyuzitim definovanych predikatu vyresi sudoku, ktere ma na vstupu zadane matici 9x9. Neobsazene policka jsou reprezentovana volnou promennou. Muzem pouzit `getIth`, `getRC`, ktery pro zadanou pozici a matici vrati prvek na tech souradnicich a `setRC`, ktery nastavi prvek v matici na nejakou hodnotu.

```prolog!
solves(M) :- search(1,1,M).

search(R,C,M) :-
    getRC(R,C,M,V),
    nonvar(V), !,
    goNonvar(R,C,M).
search(R,C,M) :-
    valsFor(R,C,M,VS),
    testVals(R,C,M,VS).
    
goNonvar(9,9,_) :- !.
goNonvar(R,C,M) :-
    newRC(R,C,RR,CC),
    search(RR,CC,M).

testVals(9,9,M,[X]) :-
    getRC(9,9,M,X), !.
testVals(R,C,M,[X|_]) :-
    getRC(R,C,M,X),
    newRC(R,C,RR,CC),
    search(RR,CC,M).
testVals(R,C,M,[_|XS]) :-
    testVals(R,C,M,XS).
```
### 1. opravny termín
Dámy
```prolog
/* -------------------------------------------------------------------- */
genAll(0,_,[]).
genAll(Size,Col,[pos(Col,Size)|Res]) :-
    Size > 0,
    SS is Size - 1,
    genAll(SS,Col,Res).
/* -------------------------------------------------------------------- */
inConf([pos(X,_)|_], pos(X,_)) :- !.
inConf([pos(_,Y)|_], pos(_,Y)) :- !.
inConf([pos(XX,YY)|_], pos(X,Y)) :-
    tst(XX,X,YY,Y), !.
inConf([_|PS], P) :-
    inConf(PS,P).
    
tst(XX,X,YY,Y) :-
    XX<X, !, tst(X,XX,YY,Y).
tst(XX,X,YY,Y) :-
    YY<Y, !, tst(XX,X,Y,YY).
tst(XX,X,YY,Y) :-
    XR is XX-X,
    YR is YY-Y,
    XR == YR.
/* -------------------------------------------------------------------- */
filterNot(_, [], []).
filterNot(Pred, [X|XS], YS) :-
    call(Pred, X), !,
    filterNot(Pred, XS, YS).
filterNot(Pred, [X|XS], [X|YS]) :-
    filterNot(Pred, XS, YS).
/* -------------------------------------------------------------------- */
tryNext(PS,Size,Possible) :-
    length(PS,LPS),
    NewCol is LPS+1,
    genAll(Size,NewCol,NewPs),
    filterNot(inConfPS),NewPs,Possible).
/* -------------------------------------------------------------------- */
findQ(Size,PS,PS) :-
    length(PS,Size),
    !.
findQ(Size,PS,[]) :-
    tryNext(PS,Size,[]),
    !, fail.
findQ(Size,PS,Res) :-
    tryNext(PS,Size,NewPS),
    tryAll(Size,PS,NewPS,Res).
tryAll(_,_,[],_) :-
    !, fail.
tryAll(Size,PS,[N|_],Res) :-
    findQ(Size,[N|PS],Res).
tryAll(Size,PS,[_|NS],Res) :-
    tryAll(Size,PS,NS,Res).
/* -------------------------------------------------------------------- */
queens(Size,Ress) :-
    setof(P,findQ(Size,[],P),Ress).
/* -------------------------------------------------------------------- */
```

### 2. opravný termín
**Obchodní cestující:**


1. Definovat predikát `search (From, To, L)`, který vyhledá nejkratší cestu z `From` do `To` a vrátí její délku v `L`. Přičemž využívá `get_distance(From,To,L)`, což je predikát pro navrácení délky cesty mezi dvěma místy.
2. Definovat predikát `fsf (+From, Where, Distances)`, který vhodně navrací nejkratší vzdálenosti od `From` do všech míst z množiny `Where` v argumentu `Distances`.
3. Definovat predikát `gc (+Distances, Closest, L)`, který vybere a vrátí nejbližší bod od `From` z předchozího bodu v argumentu `Closest` a vzdálenost k němu v `L`.
4. Definovat predikát `ts (+From, +Where, -Path, -L)`, jež dostává výchozí bod a množnu dalších, které je nutné navštívit. Musí pak vybrat nejkratší cestu a vrátit ji i její délku.


## 2017/2018
### Řádný termín
1) /6b/ Flatten seznamu - vytvorit predikat e, ktery bere 2 argumenty. Prvni je seznam libovolne zanorenych seznamu (i prazdnych), napr. ``[[], [1, 2], [[[[]]],[atom, atom]]]``. Druhy argument je vysledny seznam bez zanoreni napr. `[1, 2, atom, atom]`.
```prolog
e([],[]).
e([[]|R],Res) :- !, e(R,Res).
e([[X|XS]|YS],Res) :- !, e([X,XS|YS],Res).
e([V|XS],[V|Res]) :- e(XS,Res).

/* alternative solution */

e([],[]) :- !.
e([H|T],FlatList) :- e(H, FlatH), e(T,FlatT), append(FlatH, FlatT, FlatList), !.
e(Elem, [Elem]).

append([H1|L1], L2, [H1|R]) :- append(L1, L2, R).
append([], R, R).
```
2) /7b/ Funkce XOR, ktera vraci symterickou diferenci dvou mnozin (sjednoceni mnozin bez jejich pruseciku). Bere prvni a druhy parametr mnozinu reprezentovanou seznamem, treti parametr je vysledna mnozina reprezentovana seznamem.
```prolog
xor([],L,L) :- !.
xor(L,[],L) :- !.
xor(L,R,Res) :- sub(L,R,L1),sub(R,L,R1),app(L1,R1,Res).

sub([],_,[]).
sub([X|XS],YS,RS) :- elem(X,YS),!,sub(XS,YS,RS).
sub([X|XS],YS,[X|RS]) :- sub(XS,YS,RS).

elem(X,[X|_]) :- !.
elem(X,[_|XS]) :- elem(X,XS).

app([],L,L).
app([X|XS],L,[X|RS]) :- app(XS,L,RS).
```
3) /9b/ Napisat predikat search(PocatecniPozice, SeznamCest), ktory najde vsechny cesty z dane pozice zpet do teto pozice, delky 20 az 22 kroku (netrapit se tim, jestli vracet prvni/posledni prvek ci ne). Kazdy prvok je mozne nastivit len jeden krat vyjma prveho (== posledneho). Definicia pozicie je neznama, napiste funkci nextStep(Pos, NewPos) nad neznamym a NEKONECNYM stavovym priestorom. Mozno pouzit retract*, assert*, bagof, setof, length.
```prolog
:- dynamic pos/1.

search(P,Res) :-
    setof(Path,s(P,P,0,Path),Res).

s(P,P,N,[P]) :- N =< 22, N >= 20, !.
s(_,_,N,_) :- N > 22, !, fail.
s(P,P,N,_) :- N \= 0, !, fail.
s(A,P,N,[A|R]) :-
    assertz(pos(A)),
    NN is N+1,
    nextStep(A,AA),
    ( not(pos(AA)) ; AA=P ) ,
    s(AA,P,NN,R).
s(A,_,_,_) :-
    pos(A),
    retract(pos(A)),
    !,fail.

nextStep(p(X,Y),p(XX,Y)) :- XX is X+1.
nextStep(p(X,Y),p(XX,Y)) :- XX is X-1.
nextStep(p(X,Y),p(X,YY)) :- YY is Y+1.
nextStep(p(X,Y),p(X,YY)) :- YY is Y-1.
```
4) /8b/ Napisat predikat lookup. Prvy arguement vhodne reprezentovana tabulka symbolov, 2-hy argument kluc, 3-ty argument hodnota. A posledny a vysledny argument je modifikovana, pripadne vstupna tabulka symbolov.

Predikat pracuje v dvoch rezimoch. Ak je zadana hodnota, tak sa prida/modifikuje zaznam (klic -> hodnota?) v tabulke symbolov. Ak nie je zadana hodnota, tak vyhladavame v tabulke hodnotu so zadanym klucom. Ak sa nemylim, tak bolo mozne pouzit vsetko zo zakladnej kniznice Prologu. Ja som pouzil `var/1`, `nonvar/1` na zistenie, ci (nie) je zadana hodnota a nemyslim si, ze by to bolo v zadani spomenute. -- priklad byl mozna lehce modifikovany?
```prolog
emptyT([]).

lookup(T,_,_,_) :- var(T), !, fail.
lookup(T,Var,Val,NT) :- nonvar(Var),nonvar(Val), !, iT(T,Var,Val,NT).
lookup(T,Var,Val,T) :- nonvar(Var), lT(T,Var,Val).

% iT = insert to Table
% we have not found pair (R,L) -> insert and end
iT([], R, L, [p(R,L)]).
% we have found matching key -> update and end
iT([p(R,_)|PS], R, L, [p(R,L)|PS]) :- !.
% iterating while keys are not matching
iT([p(RR,LL)|PS], R, L, [p(RR,LL)|PPS]) :- iT(PS,R,L,PPS).

% lT = lookup in Table
lT([p(R,L)|_],R,L) :- !.
lT([_|PS],R,L) :- lT(PS,R,L).
```
### 1. opravny termín
1. Holý Prolog. Napsat predikát `mkTrans(ListOfLists,ListOfLists)`, která dostane v 1. argumentu matici, kterou transponovanou unifikuje do 2. argumentu.

```prolog
mkTrans([],[]).
mkTrans([[]|_],[]).
mkTrans(LS,[HS|HHS]) :-
    trans(LS,HS,TS),
    mkTrans(TS,HHS).

trans([],[],[]).
trans([[H|T]|LS],[H|HS],[T|TS]) :-
    trans(LS,HS,TS).
```
2. Holý Prolog. Napsat predikát `subseq`, který v 1. argumentu dostane seznam a do 2. argumentu unifikuje seznam všech jeho podseznamů, tedy jde tam o prefix a suffix matching.

```prolog
suff([],[[]]).
suff([H|T],[[H|T]|R]) :-
    suff(T,R).

subseq(S,[[]|SS]) :-
    suff(S,SUFS),
    proc(SUFS,SS).

proc([],[]).
proc([S|SS],RES) :-
    pref(S,[_|PS]),
    proc(SS,PSS),
    append(PS,PSS,RES).

pref([H|T],[[]|R]) :-
    pref(T,PS),
    prepAll(H,PS,R).
pref([],[[]]).

prepAll(_,[],[]).
prepAll(X,[L|LS],[[X|L]|XS]) :-
    prepAll(X,LS,XS).
```
3. Prolog s `bagof, setof, assert, retract` atp. Prohledávání stavového prostoru. Napsat predikát `search(Start,Cíl, Nejkratší_cesta)`, který dostane nějakou startovní a koncovou pozici a unifikuje do 3. argumentu nejkratší cestu mezi nimi. Napsat také predikát `nextStep`, který bude vracet další novou pozici. úkolem není napsat optimální řešení, ale využít elegantnosti Prologu.
```prolog
:- dynamic pos/1. % the 1 argument is really a tuple of coordinates

search(S,E,Res) :-
    retractall(pos(_)),
    steptry(S,E,0,Res).
    
steptry(S,E,N,Res) :-
    s(S,E,N,Res), !.
steptry(S,E,N,Res) :-
    NN is N+1,
    steptry(S,E,NN,Res).
    
s(E,E,0,[E]) :- !.
s(_,_,N,_) :- N < 0, !, fail.
s(A,E,N,[A|R]) :-
    assertz(pos(A)),
    NN is N-1,
    nextStep(A,AA),
    not(pos(AA)) ,
    s(AA,E,NN,R).
s(A,_,_,_) :-
    pos(A),
    retract(pos(A)),
    !,fail.
    
nextStep(p(X,Y),p(XX,Y)) :- XX is X+1.
nextStep(p(X,Y),p(XX,Y)) :- XX is X-1.
nextStep(p(X,Y),p(X,YY)) :- YY is Y+1.
nextStep(p(X,Y),p(X,YY)) :- YY is Y-1.
```
4. Prolog s celočíselným dělením, dělením a is. Napsat Prologovou reprezentaci racionálních čísel a operaci násobení a sčítání nad nimi.
```prolog
/* ---------------------------------- */
/* rac(numerartor,denominator) */
/* op('+',L,R).
* op('*',L,R).
* rac(N,D).
*/
gcd(N,N,N) :- !.
gcd(N,M,M) :-
    N > M,
    NN is mod(N,M),
    NN==0, !.
gcd(N,M,D) :- % see 'euclidean algorithm'
    N > M, !,
    NN is mod(N,M),
    gcd(M,NN,D).
gcd(N,M,N) :-
    M > N,
    MM is mod(M,N),
    MM is 0, !.
gcd(N,M,D) :-
    MM is mod(M,N),
    gcd(N,MM,D).
    
ev(op('+',L,R),rac(NN,DD)) :-
    ev(L,rac(LN,LD)),
    ev(R,rac(RN,RD)),
    N1 is LN*RD + RN*LD,
    D1 is LD*RD,
    norm(N1,D1,NN,DD).
ev(op('*',L,R),rac(NN,DD)) :-
    ev(L,rac(LN,LD)),
    ev(R,rac(RN,RD)),
    N1 is LN*RN,
    D1 is LD*RD,
    norm(N1,D1,NN,DD).
ev(rac(X,Y),rac(X,Y)).

norm(N,D,NN,DD) :-
    gcd(N,D,G), G>1,!,
    NN is div(N,G),
    DD is div(D,G).
norm(N,D,N,D).
```

## 2016/2017
### Riadny termín
1. Prohledávání stavového prostoru. Napsat predikát getPath, který má jako první parametr startovní bod a jako druhý cílový bod a do třetího unifikuje nejkratší (s nejmenší cenou) cestu mezi těmito body. Předpokládejte, že stavový prostor je konečný. Máte k dispozici predikát nextStep(X, XX, P), který jako první parametr dostane nějaký stav stavového prostoru a v druhém vrátí další stav a ve třetím bude výsledná cena tohoto kroku. (14b)

```prolog

% graph definition for testing etc.
% from: https://ibpublicimages.s3-us-west-2.amazonaws.com/tutorial/dijkstra3.png
nextStep('a', 'b', 3). nextStep('a', 'c', 2). nextStep('a', 'f', 5).
nextStep('b', 'a', 3). nextStep('b', 'c', 1). nextStep('b', 'd', 6).
nextStep('c', 'a', 2). nextStep('c', 'b', 1). nextStep('c', 'd', 4). nextStep('c', 'e', 4).
nextStep('d', 'b', 6). nextStep('d', 'c', 4). nextStep('d', 'e', 3).
nextStep('e', 'c', 4). nextStep('e', 'd', 3). nextStep('e', 'f', 2).
nextStep('f', 'a', 5). nextStep('f', 'e', 2).

:- dynamic
    visited/1,      % for preventing cycles
    best_price/1,   % when searched node is found, we save path price here for later comparison
    best_path/1.    % when we find some path to searched node, it is saved here

getPath(S,E,Path) :-  
    retractall(visited(_)),  
    retractall(best_price(_)),  
    retractall(best_path(_)),  
    assert(best_price(none)),  
    getPathHelper(S,E,Path).
    
% search always fails, but it keeps trying to explore all possible paths
getPathHelper(S,E,_) :- search(S,E,0,[]).
% eventually we fall here (no paths to explore) and check if we found some path
getPathHelper(S,_,[S|Path]) :- best_path(Path).

% when we reach the searched node, we check the price of our current path
search(E,E,Price,Path) :- checkP(Price, Path), fail.
% this is the base-case of the computation
search(S,E,P,TP) :-  
    assertz(visited(S)),    % saving the visited node
    nextStep(S,Nxt,SP),     % find a neighbouring node and price of the edge
    not(visited(Nxt)),      % make sure we have not been in this node already
    NP is P+SP,             % add the price 
    append(TP,[Nxt],NTP),   % add the node to our current path
    search(Nxt,E,NP,NTP).   % keep searching
% we fall here when we reach a dead-end = all possible neighbors have been visited
search(S,_,_,_) :-
    retract(visited(S)),    % we "unvisit" this dead-end node
    !, fail.                % and backtrack

% in this case, we have not yet found any solution
checkP(NP,Path) :-
    best_price(none),            % in which case this should be true
    retract(best_price(none)),   
    assert(best_price(NP)),      % so we set our price
    assert(best_path(Path)).     % and our path
% in this case, we have found some solution before
checkP(NP,Path) :-
    best_price(P),               % so we get its price
    NP<P,                        % we make sour our current solution is better
    retract(best_price(P)),      
    retract(best_path(_)),  
    assert(best_price(NP)),  
    assert(best_path(Path)).     % and update best price, best path   
```    

2. Holý prolog (unifikace, základní práce se seznamy, + řez tuším). Navrhnout strukturu pro ukládání boolovských výrazů (pro and, or, not) nad proměnnými a literály true/false. Dále strukturu pro ukládání hodnot proměnných. Dále napsat predikát eval(Table, Expr, Res), která vyhodnotí daný bool výraz. V table je tabulka hodnot proměnných. Expr je vyhodnocovaný výraz. Do Res se unifikuje výsledek. (6b)

```prolog
% ground truth
eval(_,true,true).
eval(_,false,false).

% cuts are necessary, so that when a evaluation of expression gives false as a result,
% we don't explore further
eval(T,var(V),R) :- getVal(T,V,R), !.
eval(T,not(E),R) :-
    eval(T,E,EvalE1),
    % we use short-circuit evaluation here
    % (also note that , has a higher priority than ; )
    % if EvalE1 is unified with true,
    % we have to also do the second part of the conjuction
    % to satisfy the first part of the or (;) expression
    % if EvalE1 is unified with false,
    % the first part of the or (;) expression cannot be true
    % because there is conjuction there, so we skip to the R=false part.
    (EvalE1, eval(_, false, R); eval(_, true, R)), !.
eval(T,and(E1,E2),R) :-
    eval(T,E1,EvalE1),
    (EvalE1, eval(T,E2,R); eval(_,false,R)), !.
eval(T,or(E1,E2),R) :-
    eval(T,E1,EvalE1),
    (EvalE1, eval(_,true,R); eval(T,E2,R)), !.

% table of bool variable values is just a list of tuples
getVal([(V,Value)|_],V,Value).
getVal([_|WS],V,Value) :- getVal(WS,V,Value).

% usage examples:
% eval([(1, true), (2, false)], not(and(not(var(1)), var(2))), X).
% eval([('a', true), ('b', false)], or((var('a'), var('b')), X).
```

3. Holý prolog (unifikace, základní práce se seznamy). Implementujte predikát msort, který provádí merge sort řazení seznamu hodnot. Musí to být merge sort s jeho klasickou složitostí (ne kvadratickou...). (7b)
```prolog
merge([],L,L).
merge(L,[],L).
merge([H1|T1],[H2|T2],[H1|TT]) :- % to place H1 at the start of the result
    H1 =< H2,                     % it has to be smaller than H2
    merge(T1,[H2|T2],TT).         % we then call merge on the rest
merge([H1|T1],[H2|T2],[H2|TT]) :- % other case (H2 is smaller)
    H2 < H1,
    merge([H1|T1],T2,TT).

msort([],[]).   % list with no items is already sorted
msort([V],[V]). % list with 1 item is already sorted
msort([A,B|T],R) :-
    divide(T,L1,L2), % split input list T into two equal-size lists
    msort([A|L1],S1), % sort both of these lists individually
    msort([B|L2],S2),
    merge(S1,S2,R). % merge them together

% items with even idx (0,2,...) go to first list, odd idxs go to second
divide([],[],[]).
divide([V],[V],[]).
divide([A,B|T],[A|TA],[B|TB]) :-
    divide(T,TA,TB).
```

4. Cely prolog, ale nesmi se pouzit vestaveny append a jeste 2 veci co si nepamatuju. Mame predikat, kde prvni parametr je predikat (dvouparametrovy - tedy jeden vstup a výsledek) a druhy je seznam seznamu. Z druheho parametru se postupně berou seznamy a ma se provest aplikace predikatu na vsechny hodnoty v seznamu a dat dohromady vysledky do jednoducheho seznamu (tedy ne seznam seznamu). (6b)

```prolog!
% we first recursively fall here, so the RES list is unified as []
mapOnListOfLists(_,[],[]) :- !.
% so that RES is in the correct order, we do the computation "backwards"
% we first have to get the whole result (recursively),
% and then apply F on the head list (HL) and 
mapOnListOfLists(F,[HL|TL],RES) :-  
    mapOnListOfLists(F,TL,RT),      % we perform F() on the rest first
    mapOnList(F,HL,RT,RES).         % then we do the head list

% once we have no more values to map a function over,
% R (3rd parameter) is unified with what was in RES (4th parameter)
mapOnList(_,[],R,R) :- !.
% base case => X contains the result with the mapped values
% it is appended to the result
mapOnList(F,[H|T],R,[X|RES]) :-
    % next two lines could also be done using `call(F, H, X),`
    C =.. [F,H,X],        % construct a predicate F(H,X)
    % we cannot do that directly since F is a variable
    call(C),              % call it (result is stored in X)
    mapOnList(F,T,R,RES).
    
% example usage:
% mapOnListOfLists(abs, [[1,2,3], [-4,5,-6], [-10], []], X).
sqr(X,Y) :- Y is X*X.
% mapOnListOfLists(sqr, [[1,2,3], [-4,5,-6], [-10], []], X).
```