Neprocedurální programování

Z ωικι.matfyz.cz
Přejít na: navigace, hledání
Neprocedurální programování
Kód předmětu: NAIL076
Přednáší: Rudolf Kryl

O předmětu

Trochu iné programovanie. Základy programovania, ktoré nepozná priraďovací príkaz. Vyučujú sa jazyky Prolog a Haskell (spomenú sa základy jazyka Scheme). Okrem Kryla tento predmet vyučuje aj Ján Hric. Pred skúškou je nutné vytvoriť zápočtový program v Prologu s dobrou dokumentáciou. V prípade cvičení s Krylom je dokumentácia možno aj dôležitejšia ako samotný program. Odporúčam vybrať si tému na program takú, pri ktorej sa dá určiť, či je program hotový (aby sa nestalo, že bude nutné pridávať stále ďalšie vlastnosti). Vhodná oblasť tém sú rozličné algoritmy, či už z ADSka alebo z automatov. Odporúča sa tiež zapísať si tento predmet v roku, keď je vyučovaný Hricom.


Řešení zkouškouvých úloh z Prologu

Hladiny binárního stromu

1) Sestavte predikáty hladiny(+Strom, -Seznam_Hladin) kde výstupní parametr je seznam seznamu prvku na jednotlivých hladinách vstupního binárního stromu Strom a k nemu inverzní strom(-Strom, +Seznam_Hladin). Nejedná se o binární vyhledávací strom, ale prvky na dané hladine jsou serazené (na jejich poradí záleží). Takže pokud mám hladinu 3 (max. 4 prvky) [a,b,c], tak tu hladinu mohu rekonstruovat jako [nil a b c], [a nil b c], [a b nil c] nebo [a b c nil], nikoli jako [b a nil c]. Pochopitelne to muže být jeden oboustranný predikát, bude-li efektivní (to inverzní musí vracet postupne všechny možnosti stromu)

Řešení: [p. Kryl toto řešení uznal]

  %hladiny(+- Strom, +- Hladiny)
  hladiny(nil, []).
  hladiny(tree(Levy, Hodnota, Pravy), [[Hodnota] | T]) :- hladiny(Levy, TL), hladiny(Pravy, TP), bmerge(TL, TP, T).
    
  bmerge([], X, X).
  bmerge(X, [], X).
  bmerge([X1|T1], [X2|T2],[X|T]) :- merge(X1, X2, X), bmerge(T1, T2, T).
  
  merge([], X, X).
  merge([X|T1], T2, [X|T]) :- merge(T1, T2, T).

Řešení by Nóža: oproti předchozímu řešení neduplikuje výsledky a při inverzi generuje všechny výsledky

  ot([],A,A).
  ot([H|T],A,S):-ot(T,[H|A],S).
  
  %vytvari novou hladinu pridanim k novemu uzlu 0-2 uzly z hladiny pod sebou
  vrstva([],[],[]).
  vrstva([X|XS],[L,R|T],[tree(L,X,R)|T0]):-vrstva(XS,T,T0).
  vrstva([X|XS],[L|T],[tree(L,X,nil)|T0]):-vrstva(XS,T,T0).
  vrstva([X|XS],[R|T],[tree(nil,X,R)|T0]):-vrstva(XS,T,T0).
  vrstva([X|XS],S,[tree(nil,X,nil)|S0]):-vrstva(XS,S,S0).
  
  %buduje strom od spodu
  vrs([],T,T).
  vrs([H|T],U,OUT):-vrstva(H,U,U0),vrs(T,U0,OUT).
  
  hladiny(nil,[]).
  hladiny(Tree,S):-var(Tree),!,ot(S,[],OS),vrs(OS,[],Tree). %pro generovani vsech stromu
  hladiny(tree(L,H,R),[[H]|OUT]):-hladiny(L,LS),hladiny(R,RS),spoj(LS,RS,OUT).
  
  spojS([],S,S).
  spojS([H|T],S,[H|OUT]):-spojS(T,S,OUT).
  
  spoj([],S,S):-!. %rez predchazejici zbytecne duplicite
  spoj(S,[],S).
  spoj([Y|YT],[X|XT],[H|OUT]):-spojS(Y,X,H),spoj(YT,XT,OUT).

Ekvivalence dvou seznamů s žolíky

2) Definujte predikát odpov(r1,r2) dvou proměnných, který pro každé dva seznamy (přirozených čísel a znaků * a ?) r1 a r2 uspěje pokud existuje "substituce jedno císla za žolík '?' a substituce posloupnosti čísel za znak '*'" takové, že dostanete stejné seznamy. Mužete předpokládat, že v každém ze seznamů, které jsou parametry, muže být nanejvýše jedna hvězdička.

Řešení #1:

  match(?,_).
  match(_,?).
  match(A,A). % :- number(A). pokud je libo :)
 
  odpov([*|A], B) :- reverse(A, RA), reverse(B, RB), odpovt(RA, RB).
  odpov(A, [*|B]) :- reverse(A, RA), reverse(B, RB), odpovt(RB, RA).
  odpov([A1|A], [B1|B]) :- match(A1, B1), odpov(A, B).
  odpov([], []).
 
  odpovt([], _).
  odpovt(_, [*|_]).
  odpovt([A1|A], [B1|B]) :- match(A1, B1), odpovt(A, B).

Řešení #2 by Martin Všeticka:

  odpov(R1,R2) :-
         cutStart(R1,R2,R1c,R2c),  % kontroluji zda se shoduji zacatky obou seznamu, dokud nenarazim na hvezdicku,
                                                % vratim zbytky obou seznamu (hvezdicku ponechavam v seznamu)
         reverse(R1c,R1cr),        % otocim seznam
         reverse(R2c,R2cr),        % otocim seznam
         cutStart(R1cr,R2cr,_,_).  % orezavam, tentokrat konce
         % cutStart bud failuje a pak seznamy nemohou byt stejne nebo nezfailuje a vratil by seznamy:
         % 1. pripad:
         % ===========
         % L1=[*,neco, neco,...]
         % L2=[neco, neco,...,*]
         % coz je pripad, kdy bychom meli vratit "true" (
         % hvezdicka v L1 se predstavuje zacatek L2 az po hvezdicku; pro hvezdicku v L2 obdobne)
         %
         % 2. pripad:
         % ===========
         % L1=[cislo/*]
         % L2=[*/cislo]
         % meli bychom vratit "true"
 
  % match
  match(X,Y):-integer(X),integer(Y),X=:=Y.
  match(X,Y):-(X == '?';Y=='?'),(integer(X);integer(Y)).
  
  % cutStart(+Sezn1,+Sezn2,-OrezanySezn1,-OrezanySezn2)
  /* pokud procedura zjisti, ze se seznamy lisi ve dvou prvcich, tak nenavratne failuje */
  cutStart([],[],[],[]):-!.       % oba dva seznamy jsou stejne (s prihlednuti k vyznamu '?') a neobsahuji hvezdicku
  cutStart([],L2,[],L2):-!,fail.  % jeden seznam je kratsi nez druhy; fail
  cutStart(L1,[],L1,[]):-!,fail.  % dtto
  cutStart([H1|T1],[H2|T2],T3,T4):-match(H1,H2),!,cutStart(T1,T2,T3,T4). % dve stejna cisla
  cutStart([H1|_],[H2|_],_,_):-integer(H1),integer(H2),H1=\=H2,!,fail.                      % dve ruzna cisla; konec
  cutStart([H1|T1],[H2|T2],[H1|T1],[H2|T2]):-(H1=='*';H2=='*'),!.                           % konec na hvezdicce

Řešení by Ivan Kuckir, asi stejné jako to první, ale bez reverse, + 2 testy, co mají vyjít pravdivě :) // ?- eqtest1(X).

  equiv([A|X], [B|Y])	:- eq(A, B), equiv(X, Y).
  equiv([*|X], L)	:- suffix(X, L).
  equiv(L, [*|X])	:- suffix(X, L).
  equiv([], []).
  
  suffix(A, B)		:- equiv(A, B).
  suffix(L, [_|Y])	:- suffix(L, Y).
  suffix([], _).
  
  eq(X, X).
  eq(?, _).
  eq(_, ?).
  
  eqtest1(true) :- equiv([1, 2, ?, 1, 4, *, 9], [?, 2, *, 3, 4, 5, 6, ?, 8, 9]).
  eqtest2(true) :- equiv([1, 2, *], [*, 4, 3, 7]).


Převod permutace na cykly

3) Prevest permutaci zadanou ve tvaru "v(7,[4,3,5,6,2,7,1])" (tedy prvni cifra udava rozsah hodnot v permutaci, zde od 1 do 7, pak nasleduje seznam, kde prvek na pozici i se zobrazi na cislo na te pozici) do zapisu ve tvaru cyklu "c(7,[[1,4,6,7],[2,3,5]])" , tedy nepsat jednoprvkove cykly.

Řešení #1:

  preved(v(L, P), c(L, CP)) :-
     ohodnot(1, P, O),
     spoj(O, CP),
     !.

  ohodnot(X, [X|T], Z) :-
     L1 is X + 1,
     ohodnot(L1, T, Z).
  ohodnot(L, [H|T], [[L,H]|Z]) :-
     L1 is L + 1,
     ohodnot(L1, T, Z).
  ohodnot(_, [], []).
 
  spoj([H|Z], O) :-
     last(H, Last),
     member([Last|X], Z),
     append(H,X,V),
     delete(Z, [Last|X], Z1),
     spoj([V|Z1], O).
  spoj([H|Z], [X|O]) :-
     append(X, [_], H), % odstrani posledni prvek
     spoj(Z, O).
  spoj([], []).

Řešení #2 by Mus:

Popis: Vezmu prvni prvek permutace, hledam pro nej cyklus, vratim permutaci, kde prvky, na kterych jsem byl, jsou 0. cyklus hledam naivne - cili vezmu dany prvek x, pridam do seznamu, k nemu xty, ap. dokud nezjistim, ze tam ten prvek uz v cyklu je.

 perm_cykly(v(0,_), c(0,[])).
 perm_cykly(v(N,P), c(N,C)) :- perm(P, P, C).
   
 %perm(+[Permutace], +[Permutace], -Cyklus)
 perm(_, [], []).
 %hledame postupne cykly pres vsechny prvky permutace
 perm(P, [HP|TP], [C|CS]) :- najdi_cyklus(HP, P, [], C, P2), !, perm(P2, TP, CS). 
 %vratil se prazdny seznam, ten v cyklech nechceme
 perm(P, [_|TP], CS) :- perm(P, TP, CS).                                          
   
 %najdi_cyklus(+Prvek, +[Permutace], +[SetridenySeznam], -[Cyklus], -[0Permtuce])
 najdi_cyklus(X, P, C1, C, P3) :- nty(X,P,NT,P2), vloz(NT, C1, C2), !, najdi_cyklus(NT, P2, C2, C, P3).
 najdi_cyklus(_, P, [], [], P) :- !, fail.  % vkladali jsme 0 -> prvek uz je v min. cyklu
 najdi_cyklus(_, P, [_], [], P) :- !, fail. % identita -> prazdny seznam
 najdi_cyklus(_, P, C, C, P).               % prvek uz je v cyklu, konec
   
 %vloz(+Prvek, +SetridenySeznam, -SetridenySeznam), vklada prvek > 0 do usp. sezn.
 vloz(0, _, _) :- !, fail.
 vloz(X, [H|T], [H|T1]) :- X > H, !, vloz(X, T, T1).
 vloz(X, T, [X|T]).
   
 %nty(+N, +Seznam, -Nty, -Seznam), vrati Nty a vrati seznam, kde na Ntem prvku je 0
 nty(1, [H|T], H, [0|T]).
 nty(N, [H|T], X, [H|T1]) :- N1 is N - 1, nty(N1, T, X, T1).

Řešení #3 by Martin Všeticka:

 % +N ... pocet permutovanych prvku
 % +Perm ... permutace, seznam, pr. [4,3,2,1] pro N = 4
 % -Cycles ... vrati: [[1, 4], [2, 3]]
 perm2cycles(N,Perm,Cycles):-
         addPos(N,Perm,PermPairs),
         place(PermPairs,CyclesPairs),
         findCycles(CyclesPairs,Cycles).
   
 % addPos(+N, +Perm, -Result)
 % Prevede permutaci [4,3,2,1] na [[1,4],[2,3],[3,2],[4,1]], tj.
 % mame tedy vzdy dvojici [x,f(x)]
 addPos(N,Perm,Result):-addPos(1,N,Perm,Result).
 addPos(N,N,[P],N,P).
 addPos(I,N,[H|PermTail],[[I,H]|Result]):-I1 is I+1, addPos(I1,N,PermTail,Result).
  
 % place(+PermPairs,-CyclesPairs)
 % Seradi za sebou dvojice [x,f(x)] timto zpusobem:
 % CyclesPairs = [ [x,f(x)], [f(x),y],[y,x], [h,i],[i,j], ...]
 % tj. vytvarime co nejdelsi posloupnosti
 place(PermPairs,CyclesPairs):-place(PermPairs,[],CyclesPairs).
 place([],L,L).
 place([[First,Second]|T],Tmp,Result):-placeWork([First,Second],Tmp,Tmp2), place(T,Tmp2,Result).
   
 placeWork([F1,S1],[],F1,S1).
 placeWork([F1,S1],[[F2,S2]|T],[[F1,S1],[F2,S2]|T]):-S1=:=F2.
 placeWork([F1,S1],[[F2,S2]|T],[[F2,S2],[F1,S1]|T]):-F1=:=S2.
 placeWork([F1,S1],[[F2,S2]|T],[[F2,S2]|R]):-placeWork([F1,S1],T,R).
   
 % findCycles(+CyclesPairs,-Cycles)
 % pr. CyclesPairs = [ [x,f(x)], [f(x),y],[y,x], [h,i],[i,j], ...]
 % predikat nalezne cykly. Prvni z prikladu je [x,f(x)], [f(x),y],[y,x].
 findCycles([],[]).
 findCycles([[Start,Start]|RestCyclesPairs],R):-findCycles(RestCyclesPairs,R). % odstraneni jednoprvkovych cyklu
 findCycles([[Start,Last]|TCyclesPairs],[Cyclus|R]):-Start=\=Last,
         findCycle(Start,Last,TCyclesPairs,Cyclus,RestCyclesPairs),
         findCycles(RestCyclesPairs,R).
   
 % findCycle(+Start,+Last,+CyclesPairs,-Cyclus,-RestOfCyclesPairs)
 % Start ... hodnota prvku, kterym jsme zacinali a ktery kdyz nalezneme
 %           znovu, tak mame cyklus
 % Last  ... hodnota prvku, na ktery chceme napojovat
 % CyclesPairs ... seznam dvojic [x,f(x)]
 findCycle(Start,Last,[[F1,S1]|RestCyclesPairs],[Last|R],Return):-Last=:=F1,
         S1=\=Start,
         findCycle(Start,S1,RestCyclesPairs,R,Return).
   
 findCycle(Start,Last,[[F1,S1]|RestCyclesPairs],[Last,Start],RestCyclesPairs):- % uspesny konec cyklu
         Last=:=F1,
         S1=:=Start.

Řešení #4 by iwtu:

 p2c(p(N,Obrazy),c(N,Cykly)) :- p2c(Obrazy,[],[],Cykly1),filter(Cykly1,[],Cykly), !.
 
 %p2c(+Obrazy, +Pouzite, ?Akumulator, -Cykly)
 p2c(O,Pou,C,C) :- length(O,LO), length(Pou,LO), !.
 p2c(O,Pou,A,CC) :- volny(O,Pou,V), cyklus(V,O,[V],C), append(Pou,C,NPou), p2c(O,NPou,[C|A],CC).
 
 %cyklus(+Start,+Obrazy,?Akumulator,-Cyklus)  
 cyklus(S,P,[H|T],[H|T]) :- obraz(S,P,H), !.
 cyklus(S,P,A,C) :- obraz(S,P,O), append(A,[O],AO), cyklus(O,P,AO,C).
 
 %obraz(+Index,+Perm,-Obraz)
 obraz(1,[O|_],O) :- !.
 obraz(N,[_|T],O) :- N1 is N-1, obraz(N1,T,O).
 
 %volny(+Zoznam1, +Zoznam2, -Prvok) -- prvy prvok, ktory obsahuje Zoznam1 ale neobsahuje Zoznam2
 volny([H|_],Z2,H) :- neobsahuje(H,Z2), !.
 volny([_|T],Z2,P) :- volny(T,Z2,P).
 
 %neobsahuje(+Prvok,+Zoznam)
 neobsahuje(_,[]).
 neobsahuje(P,[H|T]) :- P \== H, neobsahuje(P,T).
 
 %filter(+Cykly,?Akumulator,-NetrivlaneCykly)
 filter([],A,A).
 filter([H|T],A,C) :- length(H,LH), LH > 1, filter(T,[H|A],C).
 filter([H|T],A,C) :- length(H,LH), LH == 1, filter(T,A,C).

Složení permutací ve tvaru cyklů

4) Vytvořte predikát, který přijme 2 permutace v zápisu cyklu a vrátí jejich součin (tedy složení permutací)

Řešení by Martin Všetička:

  soucin(c(N,C1), c(N,C2), c(N,C3)):-      % soucin dvou permutaci
   	sezn(N,1,Res1),
   	sloz(N,C1,C2,Res1,P_tmp),
   	perm_cykly(v(N,P_tmp),c(N,C3)).
 
  sloz(_,[],_,Res1,Res1).                  % zpracuje v kazdem kroku jeden cyklus prvni permutace do vysledne permutace
  sloz(N,[[H|T]|Z],C2,Rs1,Rs):-	
   	zarad(H,[H|T],C2,Rs1,Rs2),
   	sloz(N,Z,C2,Rs2,Rs).       
 
  % v prvni permutaci se H1 zobrazi na Start
  zarad(Start,[H],C2,Rs1,Rs2):- fnd(Start, C2, X), umisti(1,H, X, Rs1, Rs2). 
  % v prvni permutaci se H1 zobrazi na H2
  zarad(Start,[H1,H2|L],C2,Rs1,Rs2):- fnd(H2,C2,X), umisti(1,H1, X, Rs1, Rs3), zarad(Start,[H2|L],C2,Rs3,Rs2). 
 
  umisti(N,N,X,[_|T],[X|T]).
  umisti(I,N,X,[H|T],[H|Rs]):-I1 is I+1,umisti(I1,N,X,T,Rs).
 
  fnd(E,[[H|T1]|_],X):-fnd0(E,H,[H|T1],X). % na co se zobrazi prvek E v druhe permutaci
  fnd(E,[_|T],X):-fnd(E,T,X).
 
  fnd0(E,X,[E],X).
  fnd0(E,_,[H1,H2|_],H2):-E==H1.
  fnd0(E,H,[H1,H2|T1],X):-E\=H1,fnd0(E,H,[H2|T1],X).
 
  %sezn(+N, 1, -[1..N]).  ... vrati seznam N nul
  sezn(N, N, [N]).
  sezn(N, X, [0|T]) :- X < N, X1 is X + 1, sezn(N, X1, T).

Řešení by Mus:

  %POSTUP: brute-force cykly->permut->soucin->permut->cykly
  %soucinc(+Cykly, +Cykly, -SoucinCyklu)
  soucinc(c(N,C1), c(N,C2), c(N,C3)) :- cykly_perm(v(N,P1),c(N,C1)),  % vrati permutaci jakozto seznam pres prvni parametr 
             cykly_perm(v(N,P2),c(N,C2)), 
             soucinp(P1, P2, P3), 
             perm_cykly(v(N,P3), c(N,C3)).
 
  %soucinp (+Permutace1, +Permutace2, -Slozena) ... slozeni dvou permutaci ulozenych v seznamech
  soucinp([], _, []).
  soucinp([P1|T1], P2, [S|TS]) :- nty(P1,P2,S), soucinp(T1, P2, TS).
 
  %nty(+N, +Seznam, -Nty, -Seznam), vrati Nty a vrati seznam, kde na Ntem prvku je 0
  nty(1, [H|_], H).
  nty(N, [_|T], X) :- N1 is N - 1, nty(N1, T, X).
 
  %cykly_perm(-Permutace, +Cykly)
  cykly_perm(v(0,_), c(0,[])).
  cykly_perm(v(N,P), c(N,C)) :- sezn(N, 1, P1), cykly_init(C, P1, P).
 
  %cykly_init(+Cykly, +TempPermutace, -Permutace)
  cykly_init([], P, P).
  cykly_init([C|TC], P1, P) :- cykl(C,C,1,P1,P2), cykly_init(TC, P2, P).
 
  %cykl(+Cyklus, +Cyklus, 1, +TempPermutace, -PermutaceDleCyklu)
  cykl([HC|_], [C|TC], N, [H|TP], [H|T2]) :- N < C, !, N1 is N + 1, cykl([HC], [C|TC], N1, TP, T2).
  cykl([HC|_], [_], _, [_|TP], [HC|TP]).
  cykl([HC|_], [_,C2|TC], N, [_|TP], [C2|T2]) :- N1 is N + 1, cykl([HC], [C2|TC], N1, TP, T2).
 
  %sezn(+N, 1, -[1..N]).  ... vrati seznam cisel od 1 do N
  sezn(N, N, [N]).
  sezn(N, X, [X|T]) :- X < N, X1 is X + 1, sezn(N, X1, T).
 

Násobení binárních čísel

5) Čísla reprezentujeme jako seznamy čísel jejich dvojkových zápisů. Sestavte predikát, který realizuje násobení čísel.

Řešení by Martin Všetička:

  binAdd(B1,B2,R):-binAdd(B1,B2,0,R1),reverse(R1,R).
  binAdd([],[],0,[]).
  binAdd([],[],1,[1]).
  binAdd([],[H2|T2],Rem,R):-binAdd([H2|T2],[],Rem,R).
  binAdd([H1|T1],[],0,[H1|T1]).
  binAdd([H1|T1],[],1,[H3|R]):-
     count(H1+1,H3,Rem2),
     binAdd(T1,[],Rem2,R).
  binAdd([H1|B1],[H2|B2],Rem,[H3|R]):-
     count(H1+H2+Rem,H3,Rem2),
     binAdd(B1,B2,Rem2,R).
  count(Sum,Show,Remainder):-Remainder is Sum//2, Show is Sum mod 2.  
 
  % cisla se zadavaji odzadu, vysledek je jiz popredu 
  binMult(B1,B2,R):-binMult(B1,B2,[],R1),reverse(R1,R).  
  binMult(_,[],R,R).
  binMult(B1,[0|B2],M,R):-binMult([0|B1],B2,M,R).
  binMult(B1,[1|B2],M,R):-binAdd(M,B1,0,M1), binMult([0|B1],B2,M1,R).

Zdroje: http://www.binarymath.info/multiplication-division.php

Řešení by Ivan Kuckir

  plus([], [], [P], P).
  plus([], [X|R], [X|R], 0).
  plus([], [H|R], NL , 1) :- plus([1], [H|R], NL, 0).
  plus([H|R], [], Res, P) :- plus([], [H|R], Res, P).
  
  plus([0|R1], [0|R2], [X|R3], P) :- ((P=1, X is 1); (P=0, X is 0)), plus(R1, R2, R3, 0).
  plus([0|R1], [1|R2], [X|R3], P) :- ((P=1, X is 0); (P=0, X is 1)), plus(R1, R2, R3, P).
  plus([1|R1], [0|R2], [X|R3], P) :- plus([0|R2], [1|R1], [X|R3], P).
  plus([1|R1], [1|R2], [X|R3], P) :- X is P, plus(R1, R2, R3, 1).
  
  times([H|R], [1], [H|R]).
  times([_|_], [0], [0]).
  times(X, [H|Rest], L) :- Rest\=[], times([0|X], Rest, V), ((H=1, plus(X, V, L, 0)); (H=0, L = V)).
  
  add     (A, B, C) :- reverse(A, RA), reverse(B, RB), plus(RA, RB, RC, 0), reverse(RC, C).
  multiply(A, B, C) :- reverse(A, RA), reverse(B, RB), times(RA, RB, RC)  , reverse(RC, C).

Řešení z http://prgs.xf.cz/:

  %binarni cisla reprezentujeme jako seznam jednicek a nul    
   
  %mult(+A, +B, -AxB)
  %v predikatu mult jsou v seznamu cisla serazene od nejvyssi vahy po nejmensi
  %cislo 12 = 1100 v programu [1,1,0,0]
  mult(A,B, Res):-
    reverse(A,RA),
    reverse(B,RB),
  	 nasob(RA, RB, RRes),
  	 reverse(RRes, Res).
   
  %reverse(+L, -ReversedL).
  reverse(L, RL):-reverse(L, [], RL).
  reverse([H|T], Acc, Res):-reverse(T, [H|Acc], Res).
  reverse([], Acc, Acc).    
   
  %nasob(+A, +B, -AxB)
  %v seznamu jsou cisla serazene od nejmensi vahy po nejvetsi
  %cislo 12 = 1100 v programu [0,0,1,1]
  nasob(A, B, Res):-
  	 nasob(A, B, 0, [], Res).
   
  %nasob(+A, +B, +PosunitieBdolava, +DocasnySucet, -Res)
  nasob(_, [], _, Acc, Acc).
  nasob(A, [0|B], N, Acc, Res):-
  	 N2 is N+1,
  	 nasob(A, B, N2, Acc, Res).
  nasob(A, [1|B], N, Acc, Res):-
  	 posun(A, N, A2),
  	 scitaj(A2, Acc, 0, Tmp),
  	 N2 is N+1,
  	 nasob(A, B, N2, Tmp, Res).
   
  %posun(+A, +PocetMist, -Aposunute_o_pocet_mist_doleva)
  posun(A, 0, A):-!.
  posun(A, N, [0|X]):-
  	 N2 is N-1,
    posun(A, N2, X).
   
  %scitaj(+A, +B, +Prechod, -Res):-
  scitaj([X|A],[Y|B], P, [Z|R]):-
  	 bs(X, Y, V, 0),
    bs(V, P, Z, P2),
    scitaj(A, B, P2, R).
  scitaj([1|A], [1|B], P, [P|R]):-
    scitaj(A, B, 1, R).
  scitaj([], B, 0, B).
  scitaj(A, [], 0, A).
  scitaj([], B, 1, R):-
     !,
     scitaj([1], B, 0, R).
  scitaj(A, [], 1, R):-
     scitaj(A, [1], 0, R).
   
  %bs(+C1, +C2, -Vysl, -Prechod)
  %binarni scitani dvou binarnich cisel 
  bs(0,0,0,0).
  bs(0,1,1,0).
  bs(1,0,1,0).
  bs(1,1,0,1).

Seznam na třetiny

6) Sestavte predikát natretiny(+Seznam,-Prvni,-Druhy,-Treti), který rozdělí vstupní seznam na tři seznamy přibližně stejné délky (Zřetězení seznamů Prvni, Druhy a Treti je seznam Seznam, délky seznamů Prvni, Druhy a Treti se mohou lišit nejvýše o 1). Při jeho konstrukci nesmíte použít žádnou aritmetiku (ani predikát length).

Řešení by Martin Všetička:

   %na3(+Seznam,-Prvni,-Druhy,-Treti)
   na3(X,R1,R2,R3):-fst3(X,X,R1,R),hlfs(R,R,R2,R3).		
   
   %hlfs(+Seznam,+Seznam,-Prvni,-Druhy)
   hlfs([],T2,[],T2).
   hlfs([_],[H|T2],[H],T2).
   hlfs([_,_|T1],[H|T2],[H|Hlf],R):-hlfs(T1,T2,Hlf,R).
   
   %fst3(+Seznam,+Seznam,-Tretina,-Zbytek)
   fst3([],T2,[],T2).
   fst3([_],[H|T2],[H],T2).
   fst3([_,_],[H|T2],[H],T2).
   fst3([_,_,_|T1],[H|T2],[H|Thrd],R):-fst3(T1,T2,Thrd,R).
   

Řešení z http://prgs.xf.cz:

   %rozdeleni seznamu na tretiny
   natretiny(L, L1, L2, L3):-
     tretiny(L, L1, L23),
     na2(L23, L2, L3).
   
   %na2(+L, -L1, -L2).
   %rozdeleni seznamu na dve poloviny
   na2(L, L1, L2):-
     na2(L,L,L1,L2).
   na2(L, [], [], L).
   na2(L, [_], [], L).
   na2([H|L],[_,_|T],[H|X],Y):-
     na2(L,T,X,Y).
   
   %prvnitretina(+L123, -L1, -L23).
   %rozdeleni seznamu na prvni tretinu a zbytek
   tretiny(L, L1, L23):-
     tretiny(L, L, L1, L23).
   tretiny(L, [], [], L).
   tretiny(L, [_], [], L).
   tretiny(L, [_,_], [], L).
   tretiny([H|L],[_,_,_|T],[H|X],Y):-
     tretiny(L, T, X, Y).

Řešení by Roman:

   tretiny_(Vz,A,B,C) :- append(Vz,Vz,Vz1), tretiny(Vz,Vz1,Vz,A,B,C).
   
   %Funkce dostane jako parametr puvodni seznam, dvojnasobne dlouhy seznam, puvodni seznam a vystupni parametry. Postupne ubira tri prvky z prvnich dvou seznamu.
   %Kdyz dojde prvni seznam znacne se ukladat do druheho vystupniho parametru, kdyz dojde druhy seznam zacne se ukladat do tretiho. 
   
   tretiny([],[],[],[],[],[]).
   tretiny([],[],[A|Vzor3],X,Y,[A|Z]) :- tretiny([],[],Vzor3,X,Y,Z).
   tretiny([],Vzor2,[A|Vzor3],X,[A|Y],Z) :- odeber_tri(Vzor2,Vzor2_), tretiny([],Vzor2_,Vzor3,X,Y,Z).
   tretiny(Vzor1,Vzor2,[A|Vzor3],[A|X],Y,Z) :- odeber_tri(Vzor1,Vzor1_), odeber_tri(Vzor2,Vzor2_), tretiny(Vzor1_,Vzor2_,Vzor3,X,Y,Z).
   
   %odebere tri prvky ze seznamu, pripadne mene pokud to nejde.
   odeber_tri([_,_,_|X],X).
   odeber_tri([_,_],[]).
   odeber_tri([_],[]).

Řešení by Peter Zeman:

   div3(List, First, Second, Third) :- div3(List, List, First, Second, Third).
   
   div3([], List2, [], Second, Third) :- div2(List2, Second, Third).
   div3([_], List2, [], Second, Third) :- div2(List2, Second, Third).
   div3([_, _], List2, [], Second, Third) :- div2(List2, Second, Third).
   div3([_, _, _ | List1], [D | List2], [D | First], Second, Third) :- div3(List1, List2, First, Second, Third).
   
   div2(List, First, Second) :- div2(List, List, First, Second).
   
   div2([], List2, [], List2).
   div2([_], List2, [], List2).
   div2([_, _ | List1], [C | List2], [C | First], Second) :- div2(List1, List2, First, Second).

Na třetiny dle počtu "modrých"

7) Máte k dispozici predikát modry/1, který uspěje, pokud argument je modrý. Sestavte predikát natretiny(+Seznam, -Prvni, -Druhy, -Treti), který rozdělí efektivním způsobem vstupní seznam na tři seznamy obsahující přibližně stejně modrých prvků (Zřetězení seznamu Prvni, Druhy a Treti je seznam Seznam, počty modrých prvků v seznamech Prvni, Druhy a Treti se mohou lišit nejvýše o 1). Při jeho konstrukci nesmíte použít žádnou aritmetiku (ani predikát length).

Řešení od univ z fora:


  % natretiny(+,-,-,-)    
  natretiny(Seznam,Prvni,Druhy,Treti):-                  % hlavni predikat
   
     prvni_tretina(Seznam,Prvni,Zbytek),
     napoloviny(Zbytek,Druhy,Treti).    
   
  % prvni_tretina(+,-,-)    
  prvni_tretina(Seznam,Prvni,Zbytek):-tretina2(Seznam,Seznam,Prvni,Zbytek).
   
  % tretina2(+,+,-,-)    
  tretina2(L1,L2,P,Zb):-
   
     odeber3modre(L1,L1Zb),!,		  % pokusim se odebrat tri modre prvky, pokud neuspeje, cela klauzule tretina2 zfailuje
     odeber1modry(L2,PZb,P,L2Zb),	  % na tri nalezene modre mi pripada jeden, ktery umistim do prvni tretiny
     tretina2(L1Zb,L2Zb,PZb,Zb).
   
  tretina2(_,L2,[],L2).
   
  % odeber1modry(+,+,-,-)       
  odeber1modry([H|L],PZb,[H|L2],Zb):-
   
     modry(H),!,L2=PZb,Zb=L;             % odeberu modry prvek
     odeber1modry(L,PZb,L2,Zb).          % .. nebo pokracuju ve zbytku seznamu
   
  % odeber2modre(+,-)    
  odeber2modre([H|L],Zb):-
   
     modry(H),!,odeber1modry(L,_,_,Zb);  % odeberu dva modre prvky
     odeber2modre(L,Zb).                 % .. nebo pokracuju ve zbytku seznamu 
   
  % odeber3modre(+,-)    
  odeber3modre([H|L],Zb):-
   
     modry(H),!,odeber2modre(L,Zb);      % odeberu tri modre prvky
     odeber3modre(L,Zb).                 % .. nebo pokracuju ve zbytku seznamu
   
   
  % napoloviny(+,-,-)    
  napoloviny(L,L1,L2):- polovina2(L,L,L1,L2).
   
  % polovina2(+,+,-,-)    
  polovina2(L1,L2,P,Zb):-
   
     odeber2modre(L1,L1Zb),!,            % stejny princip, najdu dva modre, pak jeden hned muzu zaradit do prvni poloviny
     odeber1modry(L2,PZb,P,L2Zb),
     polovina2(L1Zb,L2Zb,PZb,Zb).
   
  polovina2(_,L2,[],L2).
   
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   
  % TEST
   
  :-op(100,xfy,->).
   
  modry(m).
   
  s([m,m,a,m,m,b,m,m]).
  s([a,m,b,m,c,m,d,m,e,m,f]).
  s([a,b,c,m,m,d,e,f,m,g,h,i,m,j,k,l]).
   
  test:-s(X),natretiny(X,L1,L2,L3),write(X->L1+L2+L3),nl,fail.

Řešení by Peter Zeman:

  % Predpoklada na vstupe zoznam, obsahujuci 'r' a 'b', 'b' su modre, 'r' su
  % cervene. Uprava podla zadania je trivialna.
  
  div3blue(List, First, Second, Third) :- div3blue(List, List, First, Second, Third).
  
  % Pri div3blue/4 zalezi na poradi predikatov
  div3blue(List1, List2, First, Second, Third) :-
          take3blue(List1, _, List1Right), !,
          take1blue(List2, List2Left, List2Right),
          div3blue(List1Right, List2Right, First2, Second, Third),
          append(List2Left, First2, First).
  div3blue(List1, List2, [], Second, Third) :- take2blue(List1, _, _), !, div2blue(List2, Second, Third).
  div3blue(List1, List2, [], Second, Third) :- take1blue(List1, _, _), !, div2blue(List2, Second, Third).
  div3blue(_, List2, [], Second, Third) :- div2blue(List2, Second, Third).
  
  div2blue(List, First, Second) :- div2blue(List, List, First, Second).
  
  % Pri div2blue/4 zalezi na poradi predikatov.
  div2blue(List1, List2, First, Second) :-
          take2blue(List1, _, List1Right), !,
          take1blue(List2, List2Left, List2Right),
          div2blue(List1Right, List2Right, First2, Second),
          append(List2Left, First2, First).
  div2blue(List1, List2, [], List2) :- take1blue(List1, _, _), !.
  div2blue(_, List2, [], List2).
  
  take3blue([b | List1], [b | List1Left], List1Right) :- take2blue(List1, List1Left, List1Right).
  take3blue([r | List1], [r | List1Left], List1Right) :- take3blue(List1, List1Left, List1Right).
  
  take2blue([b | List1], [b | List1Left], List1Right) :- take1blue(List1, List1Left, List1Right).
  take2blue([r | List1], [r | List1Left], List1Right) :- take2blue(List1, List1Left, List1Right).
  
  take1blue([b | List1], [b], List1). 
  take1blue([r | List1], [r | List1Left], List1Right) :- take1blue(List1, List1Left, List1Right).

Kódování textu do matice s klíčem

8) Sestavte proceduru-y, pro kódování a dekódování (dlouhého) seznamu pomocí šifry "Monte Christo". Text se zakóduje do čtvercové matice 4x4. Kódovacím klíčem je čtvercová mřížka stejných rozměrů s vhodně vyřezanými otvory. Text se vypisuje do otvorů ve mřížce postupně po řádcích, vždy do každého otvoru jedno písmeno. Pak se mřížka otočí o 90° doprava a další text se opět vypisuje do otvorů. Toto se opakuje celkem čtyřikrát. Po zaplnění celé matice se mřížka odstraní a obsah matice se vypíše po řádcích na výstup. Je-li šifrovaná zpráva delší než jeden čtverec (tj. delší než 4x4 písmen), rozdělí se na více úseků, každý o délce jednoho čtverce. Napište kódovací a dekódovací proceduru, musíte otestovat, zda je mřížka přípustná.

Řešení by dobre_rano:

  % oboustranny predikat
  zasifruj(Text, Klic1, Sifra):-         
    len(Text, Len), Len=16,
    take4(Text,T1, Zb1), works(T1,Klic1, Sifra),
    otoc(Klic1, Klic2), take4(Zb1,T2, Zb2), works(T2, Klic2, Sifra),
    otoc(Klic2, Klic3), take4(Zb2,T3, Zb3), works(T3, Klic3, Sifra),
    otoc(Klic3, Klic4), works(Zb3, Klic4, Sifra).
     
  % works(T, Klic, Sifra) ... provadi samotne (de)sifrovani 
  works([],_,_).
  works([HP|TP], [HK|TK], [HS|TS]):-
    HK=1,!,HP=HS,works(TP, TK, TS);          % pokud se objevi policko, do ktereho mame psat
    works([HP|TP], TK, TS).                  % pokud ne.
   
  % Natvrdo napsane otoceni mrizky 4x4. 
  otoc([A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,A16],[A13,A9,A5,A1,A14,A10,A6,A2,A15,A11,A7,A3,A16,A12,A8,A4]).
 
  % Pro vsechny rotace klicove mrizky plati, ze dohromady musi vyrezana policka vsech rotaci klicove mrizky pokryt celou mrizku,
  % tudiz pokud si seznamy rotaci klice napiseme nad sebe, tak ve sloupci musi byt vzdy prave jedna jednicka
  % reprezentujici zapisovane policko tabulky, jinak by dochazelo k prepisum pri kodovani a klic by nebyl pripustny.
  zkontroluj(Klic1):-
    otoc(Klic1, Klic2), otoc(Klic2, Klic3), otoc(Klic3, Klic4),
    zkontroluj(Klic1, Klic2, Klic3, Klic4).
 
  zkontroluj([],[],[],[]).  
  zkontroluj([H1|T1], [H2|T2], [H3|T3], [H4|T4]):-
    (H1=:=1,H2=:=0,H3=:=0,H4=:=0;
    H1=:=0,H2=:=1,H3=:=0,H4=:=0;
    H1=:=0,H2=:=0,H3=:=1,H4=:=0;
    H1=:=0,H2=:=0,H3=:=0,H4=:=1),
    zkontroluj(T1,T2,T3,T4).
  
  % Vezmi ctyri prvky ze seznamu 
  take4(Text,Ctyri,Zbytek):-
    takeN(4, Text, Ctyri, Zbytek).
   
  take4(0, L, [], L).
  take4(_, [], [],[]).
  take4(N, [H|L], [H|Zb], Rest):- 
    N > 0, N1 is N -1,
    takeN(N1, L, Zb, Rest).
                                       
  len([],0).
  len([H|T], N):-
    len(T,N1), N is N1+1.

Algebrogramy

9) Číslo reprezentujeme jako seznam jeho cifer v desítkové soustavě. Sestavte program, který pro 3 seznamy cifer a písmen zjistí, zda existuje substituce písmen za cifry taková, že součet prvních dvou seznamů se rovná třetímu seznamu. Stejné písmeno se může vyskytovat vícekrát (nahradíme ho vždy stejnou cifrou).

Řešení by Ivan Kuckir + testy

Vstup: 3 seznamy cifer / písmen

Výstup: seznam dvojic písmeno-číslo (substituce)

Algoritmus: procházím čísla od konce, na každé úrovni zkusím substituovat za 0 až 3 znaky,

provedu substituci na zbytku a pustím algoritmus na tento zbytek

   algebr(A, B, C, Seq) :-	reverse(A, RA), reverse(B, RB), reverse(C, RC),
   				fillZero(RA, RC, NA, NC), fillZero(RB, RC, NB, _), algRec(NA, NB, NC, 0, Seq).
   
   algRec([A|At], [B|Bt], [C|Ct], Z, Seq) 	:-	getSub(A, B, C, Z, NZ, S),
   							makeSub(S, At, NAt), makeSub(S, Bt, NBt), makeSub(S, Ct, NCt), 
   							algRec(NAt, NBt, NCt, NZ, NS), concat(S, NS, Seq).
   algRec([], [], [], 0, []).
   
   % najde seznam 0 až 3 substitucí pro dané A, B, C a Zbytek z předchozího řádu
   getSub(A, B, C, Z, NZ, [A-X|T]) :-	atom(A), !, cif(X), oneSub(A-X, [B, C], [NB, NC]),	getSub(X, NB, NC, Z, NZ, T).
   getSub(A, B, C, Z, NZ, [B-X|T]) :-	atom(B), !, cif(X), oneSub(B-X, [C], [NC]),		getSub(A, X, NC, Z, NZ, T).
   getSub(A, B, C, Z, NZ, [C-X|T]) :-	atom(C), !, cif(X),					getSub(A, B, X, Z, NZ, T).									
   getSub(A, B, C, Z, NZ, []) :-	X is (A+B+Z) mod 10, X == C, NZ is (A+B+Z) div 10.
   
   % provede seznam substitucí na seznamu							
   makeSub([S|St], A, Na) :- oneSub(S, A, X), makeSub(St, X, Na).
   makeSub([], X, X).
   % provede jednu substituci na seznamu
   oneSub(C-N, [C|Ct], [N|NCt]) :- oneSub(C-N, Ct, NCt).
   oneSub(C-N, [X|Xt], [X|NXt]) :- X \= C, oneSub(C-N, Xt, NXt).
   oneSub(_, [], []).
   
   % doplní kratší ze seznamu nulami, aby byly stejně dlouhé
   fillZero([A|At], [B|Bt], [A|NAt], [B|NBt]) :- fillZero(At, Bt, NAt, NBt).
   fillZero([], [B|Bt], [0|NAt], [B|NBt]) :- fillZero([], Bt, NAt, NBt).
   fillZero([A|At], [], [A|NAt], [0|NBt]) :- fillZero(At, [], NAt, NBt).
   fillZero([], [], [], []).
   
   % vrací cifry 0 až 9
   cif(X) :- rcif(X, 9).
   rcif(0, 0) :- !. rcif(X, X). 
   rcif(C, N) :- NN is N-1, rcif(C, NN).
   atest1(X) :- algebr([1,1,a], [2,b,a], [3,4,6], X). 					%%%%	X = [a-8, b-2] ; X = [a-3, b-3] ;
   atest2(X) :- algebr([1,c,a], [4,3,b,c], [4,4,4,6], X).				%%%%	X = [a-6, c-0, b-4] ; X = [a-5, c-1, b-3] ; ...
   atest3(X) :- algebr([a], [a], [c], X).   						%%%%	X = [a-4, c-8] ; X = [a-3, c-6] ; X = [a-2, c-4] ; ...
   atest4(X) :- algebr([o,k,l,a,m,a,l], [v,a,p,e,n,i,k], [k,o,m,i,n,i,k,a], X),.	%%%%	X = [l-8,k-1,a-9,i-2,m-1,n-0,e-1,p-3,o-9,v-9], ...


Vyvážený binárny strom

10) Mame rastuci zoznam a chceme vyvazeny binarny strom o N prvkov a Zvysok. Predpokladame, ze zoznam je velky aspon N.

 %postav(+Zoznam,+N,-Strom,-Zvysok)
 postav([],_,nil,[]).
 postav(R,0,nil,R).
 postav(Z,N,t(LS,V,PS),Zv) :- N1 is N - 1, NL is N1 div 2, NP is N1 - NL,     
                              postav(Z,NL,LS,[V|Zv1]), postav(Zv1,NP,PS,Zv).

Řešené úlohy z haskellu

1) Definujte si vhodným způsobem datovou strukturu pro reprezentaci orientovaného grafu. Vytvořte funkci (definovanou na všech grafech), která vrátí topologické uspořádání grafu nebo sdělí, že topologicky uspořádat nejde.

Řešení by Martin Všetička:

   {-
   Popis algoritmu: Topologické uspořádání grafu: Máme orientovaný graf G s N vrcholy a chceme očíslovat vrcholy 
   čísly 1 až N tak, aby všechny hrany vedly z vrcholu s větším číslem do vrcholu s menším číslem, tedy aby pro každou 
   hranu e = (vi, vj) bylo i > j. Představme si to jako srovnání vrcholů grafu na přímku tak, aby "šipky" 
   vedly pouze zprava doleva.
   
   Cyklus je to jediné, co muže existenci topologického uspořádání zabránit. Libovolný acyklický graf 
   lze uspořádat následujícím algoritmem:
   
   1. Na začátku máme orientovaný graf G a proměnnou p = 1.
   2. Najdeme takový vrchol v, ze kterého nevede žádná hrana (budeme mu říkat stok). 
      Pokud v grafu žádný stok není, výpočet končí, protože jsme našli cyklus.
   3. Odebereme z grafu vrchol v a všechny hrany, které do něj vedou.
   4. Přiřadíme vrcholu v číslo p.
   5. Proměnnou p zvýšíme o 1.
   6. Opakujeme kroky 2 až 5, dokud graf obsahuje alespoň jeden vrchol.
   
   zdroj: http://ksp.mff.cuni.cz/tasks/18/cook2.html
   -}
   
   
   data Vertex a   = Vertex a [a] deriving Show
   data  (Eq a) => Graph a    = Vertices [Vertex a]
   
   topSrch :: (Eq a) => (Graph a) -> [a]
   
   topSrch g = reverse $ topSrch1 g 
   
   topSrch1 :: (Eq a) => (Graph a) -> [a] 
   topSrch1 (Vertices [])    = []
   topSrch1 g@(Vertices lst) = (lastOne):(topSrch1 (Vertices lst3))
      where lastOne = fndLast lst
            lst2 = rmVertex lst 
            lst3 = remEdges lst2 lastOne 
   
   fndLast :: [Vertex a] -> a 
   fndLast []                 = error "fndLast: No last vertex. It's not possible to topologically sort the graph"
   fndLast ((Vertex a []):xs) = a
   fndLast (x:xs)             = fndLast xs  
   
   rmVertex :: [Vertex a] -> [Vertex a] 
   rmVertex []                 = []
   rmVertex ((Vertex a []):xs) = xs
   rmVertex (x:xs)             = x:(rmVertex xs)  
   
   
   remEdges :: (Eq a) => [Vertex a] -> a -> [Vertex a]
   remEdges [] _ = [] 
   remEdges ((Vertex nm1 edg1):xs) v = ((Vertex nm1 (filter (/= v) edg1)):(remEdges xs v))  
   
   
   {-
   Testy:
   
   topSrch (Vertices [(Vertex 'a' ['b']),(Vertex 'b' ['c']),(Vertex 'c' ['d']), (Vertex 'd' ['a'])])  -- obsahuje cyklus
   topSrch (Vertices [(Vertex 'a' ['b']),(Vertex 'b' ['c']),(Vertex 'c' ['d']), (Vertex 'd' [])])  -- neobsahuje cyklus 
   -}

Řešení z http://prgs.xf.cz/pis080107/topolog_tried.hs:

   type Graf a = [(a,Int,[a])]
   --(vrchol,pocet predchodcov, zoznam nasledovnikov)
   
   sort::Graf a->Graf a
   --zotriedenie grafu podla poctu predchodcov
   sort [] 		= []
   sort ((v,pp,zn):xs)	= (sort [s|s@(a,b,c)<-xs, b<pp])
   			  ++ [(v,pp,zn)]
   			  ++ (sort [s|s@(a,b,c)<-xs, b>=pp])
   
   usp::(Eq a)=>Graf a->(Bool, [a])
   --usp graf z => graf topologicky usporiada do zoznamu a tento pripoji k zoznamu z
   --predpokladame, ze g je zotriedeny funkciou sort
   usp [] 			= (True, [])
   usp (h@(v,pp,zn):xs)  	= if pp>0 then (False, [])
   			  else let (res,t) = usp (sort (zniz zn xs))
   			       in (res,v:t)
   
   zniz::(Eq a)=>[a]->Graf a->Graf a
   --zniz nasledovnikom v grafe pocet predchodcov o jedna
   zniz znasl g		= [(v,pp-1,zn)|(v,pp,zn)<-g, znasl `obsahuje` v]
   			  ++[s|s@(v,pp,zn)<-g, not (znasl `obsahuje` v)]
   			  where 
   			  	obsahuje::(Eq a)=>[a]->a->Bool
   				(x:xs) `obsahuje` v	= if x==v then True
   							  else xs `obsahuje` v
   				[] `obsahuje` v		= False
   
   topol::(Eq a)=>Graf a->[a]
   --vrati topologicke usporiadanie grafu, ak graf obsahuje kruzniu, vrati prazdny zoznam
   topol g			= let (res,t) = usp (sort g)
   			  in if res==True then t else []   sort []  = []
      sort ((v,h):xs) = sort [(a,b)|(a,b)<-xs, b<h]
                        ++ [(v,h)]
                        ++ sort [(a,b)|(a,b)<-xs, b>=h]
           
      nodes::Tree a->[a]
      --vrati seznam vrcholu stromu setridenych podle vzdalenosti od nejblisiho listu, ktery je pod nim
      nodes t = [v|(v,h)<-sort (ohodnot t)]
      --nodes strom vrati "aeghbdfc"

Řešení by Ivan Kuckir. Pokud nejde topologicky uspořádat, vrátí kružnici (podgraf).

   data Node   = Node NodeID String [NodeID] deriving (Show)
   type NodeID = Int
   type Graph  = [Node]
   
   getID :: Node -> NodeID
   getID (Node x _ _) = x
   
   getNs :: Node -> [NodeID]	-- následníci
   getNs  (Node _ _ l) = l
   
   getNodeByID :: Graph -> NodeID -> Node	-- najde vrchol podle ID
   getNodeByID (x:t) id
   		| (getID x)==id	= x
   		| otherwise 	= getNodeByID t id
    {-
   	Topologické uspořádání grafu
   	vstup: Souvislý orientovaný graf (seznam vrcholů)
   	výstup: seznam dvojic (Vrchol : číslo) topologické uspořádání
   			seznam dvojic (Vrchol : 0), na nichž je cyklus
   	Algoritmus: while Existuje vrchol bez následníků
   					odeberu ho
   					odeberu hrany vedoucí do něj
   				pokud neexistuje, půjdu někam po hranách, než najdu cyklus
   -}
   
   topo :: Graph -> [(NodeID, Int)]
   topo g = topoRec g 1
   
   topoRec :: Graph -> Int -> [(NodeID, Int)]		-- "topo" rekurzivně
   topoRec g@(h:t) n
   		| eid == -1		= getCircle g
   		| otherwise		= (eid, n) : (topoRec (graphWithout g eid) (n + 1))
   		where eid = getEmpty g			-- eid = empty ID
   topoRec [] _ = []
   
   graphWithout :: Graph -> NodeID -> Graph		-- smaže z grafu vrchol i hrany vedoucí do něj
   graphWithout ((Node x s l):t) id 
   		| x == id	= graphWithout t id
   		| otherwise = (Node x s (remove id l)) : (graphWithout t id)
   graphWithout [] _ = []
   
   remove :: NodeID -> [NodeID] -> [NodeID]		-- smaže ze seznamu první výskyt prvku
   remove id (h:t)
   		| id == h	= t
   		| otherwise	= h:(remove id t)
   remove id [] = []
   
   getEmpty :: Graph -> NodeID				-- najde v grafu vrchol bez následníků (hran směrem ven)
   getEmpty ((Node id s l):t)
   		| l == []	= id
   		| otherwise = getEmpty t
   getEmpty [] = -1
   
   getCircle :: Graph -> [(NodeID, Int)]		-- najde v grafu kružnici
   getCircle ((Node id s l):t) = (id, 0) : (getCircleRec (head l) id ((Node id s l):t))
   
   getCircleRec :: NodeID -> NodeID -> Graph -> [(NodeID, Int)]	-- "getCircle" rekurzivně
   getCircleRec x start g 
   	| x == start	= []
   	| otherwise		= (x, 0) : (getCircleRec nb start g)
   	where nb = head (getNs (getNodeByID g x))	-- nb = první neighbour

2) Definujte typ vhodný k reprezentaci multimnožiny desítkových cifer (každá z cifer se v ní může vyskytovat vícekrát). Uvažme čísla, která lze sestavit z cifer takové multimnožiny (nemusíme použít všechny). Naprogramujte dvě funkce:

a) první, která nalezne k číslu N a multimnožině M N-té takové číslo b) druhou "inverzní", která k takovému číslu X spočítá jeho pořadové číslo

Řešení:

  a) 
  
     TODO   
  
  b)
  
     TODO      

3) Všechna k-ciferná čísla, v jejichž dekadickém zápisu jsou všechny cifry různé, jsme "myšlenkově" seřadili podle velikosti. Napište procedury či funkce, které k zadanému číslu přímo spočtou jeho pořadí a naopak na základě pořadí naleznou příslušné číslo.

Řešení: [najde pouze další permutaci]


   quicksort :: (Ord a) => [a] -> [a]  
   quicksort [] = []  
   quicksort (x:xs) =   
      let smallerSorted = quicksort [a | a <- xs, a <= x]  
          biggerSorted = quicksort [a | a <- xs, a > x]  
      in  smallerSorted ++ [x] ++ biggerSorted  
   
   
   -- 1. param je posledni hodnota, 2. je soucasne minimum z prochazenych prvku
   fnd        :: (Ord a) => Int -> a -> [a] -> Int        
   fnd n z (x:xs)  
        |  z < x && xs /= []  =  fnd (n+1) x xs 
        |  z > x              =  n 
        |  otherwise          =  error "zadna dalsi permutace"        -- error state              
   
   -- Myslenka je takova, ze otocime seznam s permutaci, hledame prvni cislo X takove, ze je vetsi nez predchozi cislo,
   -- najdeme minimum v casti pred X a prohodime toto cislo s X v seznamu a setridim cast pred X.  
   nepe         :: (Ord a,Show a) => Int -> [a] -> [a]    
   nepe k lst    = (take (n-2) lst)  ++ [min'] ++ (quicksort (filter (/= min') (take (j+1) revLst)))   
                   where (y:ys) = reverse lst
                         revLst = (y:ys)
                         j      = fnd 1 y ys  
                         n      = k - j + 1
                         min'   = (minimum  (filter (> (revLst !! j)) (take j revLst)))   

Řešení #2 pomocí algoritmu z přednášky:

   naslPerm  :: (Ord a) => [a] -> [a]
   naslPerm lst  =  reverse zbytek ++ (z:lst3)
   
         where lst2              = reverse lst
               (rZac, y, zbytek) = rostZac lst2
               (lst3, z)         = zarad rZac y
                
                
   rostZac  :: (Ord a) => [a] -> ([a],a,[a])
   rostZac []       = error "Prazdny seznam neni povolen"
   rostZac (x:y:xs)   
         | x > y    = ([x],y,xs)
         | x < y    = ((x:sezn),z,zbytek)     
     where (sezn,z,zbytek) = rostZac (y:xs)
   
   zarad  :: (Ord a) => [a] -> a -> ([a],a)  
   zarad (x:xs) y   
       | y < x     = (y:xs, x)   -- x je nejmensi vetsi nez y 
       | y > x     = (x:sezn, z)  
     where (sezn, z)  = zarad xs y
  

4) Řídká matice je reprezentována jako trojice (m,n,s), kde m a n jsou rozměry matice a s je seznam trojic (i,j,aij) - i,j souřadnice, a_{ij} nenulové číslo na těch souřadnicích - uspořádany vzestupně podle i a uvnitř řádek podle j.

Naprogramujte: a) transpozici b) násobení 2 matic

Řešení by Mus:

  type Matice = (Int, Int, [Souradnice])    -- synonymum
  type Souradnice = (Int, Int, Int)
   
  {- a) transpozici      -} 
  {- =================== -}
  transpozice :: Matice -> Matice
  transpozice (m, n, []) = (n, m, [])
  transpozice (m, n, s) = (n, m, quicksort [(j, i, aij) | (i, j, aij) <- s])
  -- transpozice (m, n, s) = (n, m, sort [(j, i, aij) | (i, j, aij) <- s]) 
   
  sort [] = []
  sort (x:xs) = sort [a | a <- xs, a <= x] ++ [x] ++ sort [a | a <- xs, a > x]
   
  quicksort :: (Ord a) => [a] -> [a]  
  quicksort [] = []  
  quicksort (x:xs) =   
      let smallerSorted = quicksort [a | a <- xs, a <= x]  
          biggerSorted = quicksort [a | a <- xs, a > x]  
      in  smallerSorted ++ [x] ++ biggerSorted  
 
  -- Test: transpozice (4,4,[(1,3,3),(2,2,4),(3,4,5)])  
  -- Vysledek: (4,4,[(2,2,4),(3,1,3),(4,3,5)])
 
  {- b) násobení 2 matic -} 
  {- =================== -}
   
  -- Cij = Suma k=1..n : Aik * Bkj
  
  vynasob :: Matice -> Matice -> Matice
  vynasob (m, n, a) (o, p, b) | n /= o    = error "matice nelze nasobit" 
                              | otherwise = (m, p, secti (srovnej [(i, j, aik*bkj) | (i, k1, aik) <- a, (k2, j, bkj) <- b, k1 == k2 ]))
 
  -- seradi seznam trojic (a,b,c) tak, ze za sebou jsou trojice se stejnymi a a b
  srovnej :: [Souradnice] -> [Souradnice]
  srovnej [] = []
  srovnej [a] = [a] 
  srovnej ((i1, j1, aij1):s) = [(i1, j1, aij1)] ++ 
          [ (i, j, aij) | (i, j, aij) <- s, i == i1, j == j1 ] ++       
          srovnej [ (i, j, aij) | (i, j, aij) <- s, (i /= i1 || j /= j1) ]
 
  secti :: [Souradnice] -> [Souradnice]
  secti [] = []
  secti [a] = [a] 
  secti ((i1, j1, aij1):(i2, j2, aij2):s) 
   | i1 == i2 && j1 == j2       =    if aij1 + aij2 /= 0 then secti ((i1, j1, aij1 + aij2):s) 
                                                         else secti s
   | otherwise = [(i1, j1, aij1)] ++ secti ((i2, j2, aij2):s)


  -- WIKI: http://en.wikipedia.org/wiki/Sparse_matrix      

5) Vytvořte funkci, která pro (nekonečný) seznam reálných čísel [xi] vstup a (krátký) konečný rostoucí seznam intervaly malých integeru [a1,a2,...,an] - představte si třeba [2,5,10,25] vytvoří nekonečný seznam, jehož k-tý prvek je "n-tice" klouzavých průměrů s intervaly [a1,a2,...,an] konče k-tým.

Jiná formulace zadání: Sestavte funkci, která ke vstupujicí (nekonečné) posloupnosti reálných čísel a přirozenému číslu N vydá posloupnosti "klouzavých průměrů s intevalem N". Klouzavý průměr s intervalem N je průměr posledních prvků.

Řešení:

Klouzave prumery - urcite pomocnou fci (nebo lambda), ktera dostane dvakrat seznam (puvodni a puvodni bez prvnich n clenu), soucasnou sumu a dane N a bude nove soucty pocitat pomoci tech predchozich. Co jsem slysel, nelibi se mu, kdyz to clovek pokazde pocita znovu.

7) Mocninna rada je reprezentovana (nekonecnou posloupnosti) posloupnosti jejich koeficientu. Vytvorte funkce, ktere spocitaji a) Soucet dvou mocninnych rad b) Soucin dvou mocninnych rad c) K-tou derivaci mocninne rady

Řešení by Martin Všetička:

  a) 
   
     soucet  s t         = [a+b | (a,b) <- zip s t]   -- verze 1    
     soucet2 (a:s) (b:t) = ((a+b):soucet2 s t)        -- verze 2
     
     -- pokud nescitame radu, ale polynomy:      
     add                 :: (Num a)=>[a]->[a]->[a]
     add [] []           =  []                        -- neni potreba pokud jde o mocninou radu
     add (x:xs) []       =  x:xs                      -- dtto
     add [] (y:ys)       =  y:ys                      -- dtto
     add (x:xs) (y:ys)   =  (x+y):(add xs ys)                
  b) 
     zipWith'            :: (a -> a -> a) -> [a] -> [a] -> [a]  
     zipWith' f [] _     = []  
     zipWith' f _ []     = []  
     zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
     
     product                   :: (Num a)=>[a]->[a]->[a]
     product series1 series2  = [sum (zipWith' (*) xs ys ) | n <- [1,2..], 
                                 let xs = take n series1, let ys = reverse (take n series2)]       
     Jiny zpusob:
     
     let (+++) = zipWith (+); 
     (f:fs) *** (g:gs) = f*g : (map (*f) gs +++ map (*g) fs +++ (0: fs***gs))  in  [1..] *** [1..]
     
     (source: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.38.9450 - chapter 2.2)
     Jiny zpusob:
     
     nasob a b = nas a b []    
     nas (an:as) b rev = [ sum( [ x*y | (x,y) <- zip b (an:rev)] ) ] ++ (nas as b (an:rev) )
         
  c)     
     -- 1. zpusob
     derivative              :: (Num a,Enum a)=>Integer->[a]->[a]
     derivative  n lst = if n > 0 then 
                                     derivative (n-1) (zipWith (*) [1,2..] (drop 1 lst))
                                  else
                                     lst                          
     
     -- 2. zpusob      
     der (a:s) k = (k*a):der s (k+1)      
     
     derivace   (a:s) =  der s 1                        -- jednoducha derivace (odeberu konstantu ze zacatku a nasobim v der)        
     derivace_k s 0   =  s                              -- 0-ta derivace
     derivace_k s k   =  derivace_k (derivace s) (k-1)  -- k-ta derivace


8) Reprezentovat BVS a pak funkci na vypousteni vsech vrcholu s hodnotou mezi Min a Max:

Řešení:

  data (Ord a, Eq a, Num a) => BVS a   =  Null | Node (BVS a) a (BVS a) deriving Show
   
  inorder :: (Ord a, Eq a, Num a) => BVS a -> [a]
  inorder Null = []
  inorder (Node left a right) = inorder left ++ [a] ++ inorder right
   
  min'        :: (Ord a, Eq a, Num a) => BVS a -> a
  min'        =  head . inorder
   
  max'        :: (Ord a, Eq a, Num a) => BVS a -> a
  max'        =  head . inorder
   
  {- Pro jeden prvek -}
  deleteX       ::  (Ord a, Eq a, Num a) => a -> (BVS a) -> (BVS a)
 
  deleteX e Null = Null    
  deleteX e (Node Null a Null) 
        | e == a     =   Null
        | otherwise  =   Node Null a Null
            
  deleteX e (Node left a Null) 
      | a == e    = left
      | otherwise = (Node (deleteX e left) a Null) 
 
  deleteX e (Node Null a right) 
      | a == e    = right
      | otherwise = (Node Null a (deleteX e right)) 
 
  deleteX e (Node left a right) 
      | a == e    =  Node  (deleteX (max' left) left) (max' left) right 
      | a <  e    =  Node left a (deleteX e right)  
      | a >  e    =  Node (deleteX e left) a right
     
  {- Test: deleteX 3 (Node (Node Null 1 Null) 2 (Node (Node Null 3 Null) 3 Null))  -}
  
  {- viceprvkova verze: -}
  
  -- vypusti ze stromu vsechny uzly od m do n
  remBVS :: (Ord a) => BVS a -> a -> a -> BVS a
  remBVS Null _ _ = Null
   
  remBVS (BVS (Null,val,Null)) m n
          | m <= val && val <= n        =   Null
          | otherwise                   =   BVS (Null,val,Null)
   
  remBVS (BVS (left,val,right)) m n = 
          if m>n then Null
          else if n < val then BVS (remLeft,val,right)
          else if val < m then BVS (left,val,remRight)
          else if not (remLeft==Null)  then BVS (remLeftMax, maxremLeft, remRight)
          else if not (remRight==Null) then BVS (remLeft, minremRight, remRightMin)
          else Null
                  where remLeft     = remBVS left m n
                        remRight    = remBVS right m n
                        maxremLeft  = findMax remLeft
                        minremRight = findMin remRight
                        remLeftMax  = remBVS remLeft maxremLeft maxremLeft
                        remRightMin = remBVS remRight minremRight minremRight


  Zdroj: http://s0cketka.blogspot.com/2006/01/haskell-male-ulozky.html 
  Zdroj: http://blog.geeksynapse.net/29/en/

9) Soucin a podil dlouhych cisel - dlouhe cislo je trojice Znamenko, [Int], Pozice desetinne carky od prvni cifry

  Násobení by Tomáš Slavíček (nešlo mi to sem vložit, rozpadal se layout): http://pastebin.com/331Ka7fX

10) Úloha BATOH: Je dán seznam A číslo N. Napište funkci, která zjistí, zda je možné posčítat (některé) prvky seznamu, aby součet vyšel N.

Řešení:

  -- nalezeno na s0cketce: http://s0cketka.blogspot.com/
  -- implementujeme funkci, ktera tento seznam vrati, dana uloha pak by byla
  -- pokud seznam neexistuje, vraci se []
  batoh      :: [Int] -> Int -> [Int]
  batoh _ 0  = []
  batoh [] _ = []
  batoh (x:xs) n
   | sum newBatoh1 == n = newBatoh1
   | sum newBatoh2 == n = newBatoh2
   | otherwise = []
         where newBatoh1 = x:(batoh xs (n-x))
               newBatoh2 = batoh xs n                  

11) Definujte prirozenou reprezentaci binárních stromu, v jehož uzlech je uložena informace nejakého typu (podtrídy Ord). Sestavte funkci, která na základe rostoucího (!) seznamu S a císla N vytvorí z prvních N prvku tohoto seznamu dokonale vyvážený binární vyhledávací strom T (pro každý uzel platí, že velikost L a P podstromu se liší nejvíc o 1) a spolu s tímto stromem vrátí i seznam, který zbyl ze seznamu S po postavení stromu T, tj. S bez prvních N clenu.

Řešení od HonzyK:

   data (Ord a) => BST a    =    Null | Node (BST a) a (BST a)    
   middle_element :: (Ord a) => [a] -> a      -- returns middle element from the list        
   middle_element x = last y
                  where y = take (((length x) `div` 2) + 1) x
      
                  
   build_tree :: (Ord a) => [a] -> BST a    -- builds perfectly balanced BST    
   build_tree [] = Null
   build_tree (h:[]) = Node Null h Null    
   build_tree x = Node leftOne h rightOne
                  where h        = middle_element x
                        leftOne  = build_tree [ y | y <- x, y < h]
                        rightOne = build_tree [ y | y <- x, y > h]
                        
   
   build :: (Ord a) => [a] -> Int -> (BST a, [a])    
   build x 0  = (Null, x)
   build [] _ = error "Nothing in list"
   build s n | (length s) < n = error "List S contains less than N elements"
             | otherwise      = ( build_tree (take n s), drop n s )             

Řešení by Mus:

   -- Binarni Strom
   data (Ord a) => BTree a = Null | Node (BTree a) a (BTree a) deriving Show
   
   createBTree :: (Ord a) => [a] -> Int -> ((BTree a), [a])
   createBTree [] _ =  (Null, [])
   createBTree s 0 =  (Null, s) 
   createBTree [s] n = ((Node Null s Null), []) 
   
   createBTree s n = ((Node l v r), rest)
                     where
                     n1 = n - 1
                     nr = n1 `div` 2
                     nl = n1 - nr
                     (l, (v:rs)) = createBTree s nl
                     (r, rest) = createBTree rs nr 
      
  
   -- casova slozitost: O(N)   


12) Definujte přirozenou reprezentaci binárního stromu, v jehož uzlech je uložena informace nějakého typu (podtřídy Ord). Naprogramujte funkci, která ze zadaného binárního vyhledávacího stromu vypustí všechny uzly, které obsahují hodnotu klíče, na kterém zadaná funkce `krit' vrátí hodnotu True.

Řešení od univ z fóra:

  data Tree a    =   Nil | ND (Tree a) a (Tree a) deriving Show
 
  instance Eq (Tree a) where        -- na porovnavani stromu s Nilem  
      Nil == Nil = True      
      _ == _ = False      
 
  vypust::(a->Bool)->Tree a->Tree a 
 
  vypust _ Nil = Nil  
  vypust krit (ND l v p)
    | not (krit v) = (ND lt v pt)
    | lt == Nil    = pt
    | pt == Nil    = lt
    | otherwise    = (ND ltBEZmax maxlt pt)
     where
       lt  =  vypust krit l
       pt  =  vypust krit p
       (ltBEZmax,maxlt) =  bezMAX lt
      
 
  bezMAX::  Tree a->(Tree a,a)
  bezMAX (ND l v p)
    | p == Nil  = (l,v)        -- pro toto porovnani jsme definovali vyse "instance ..."
    | otherwise = ((ND (l) (v) (pt)), maxpt)
     where
    (pt,maxpt) = bezMAX p

Užitečné algoritmy

  • následující permutace:
 % vyda nasledujici permutaci v lexikografickem poradi
 naslperm(Perm, NPerm) :- 
          reverse(Perm, OtPerm), 
          rozloz(OtPerm, RostZac, X, Zbytek), 
          zarad(RostZac, X, Y, NRostZac), 
          concrev(Zbytek,[Y|NRostZac], NPerm).
 
 % rozloz(+Perm,-RostZac,-KlesPrvek,-Zbytek)
 rozloz([X,Y|T],[X|T1],A,Z):-X<Y,rozloz([Y|T],T1,A,Z).
 rozloz([X,Y|T],[X],Y,T):-X>Y.
 
 % zarad(+Rost,+Vloz,-NejmenšíVětšíNežX,-RostPoVýměně)
 zarad([A|Rost],X,Y,[A|NRost]):- A < X, zarad(Rost,X,Y,NRost).
 zarad([A|Rost],X,A,[X|Rost]) :- A > X.          
 %  ============================================================== 
 %  concrev(+L,+T,-S)  S je zřetězení obráceného seznamu L 
 %                       se seznamem T 
 %      tedy predikát ot(+L,-S):- concrev(L,[],S) otáčí seznam 
 %      ?-concrev([a,b,c],[1,2],S).    S=[c,b,a,1,2] 
 %  ============================================================== 
 concrev([],L,L). 
 concrev([X|T],L,P):- concrev(T,[X|L],P). 
          
	% pozn. algoritmus selze, pokud dalsi permutace v lex. poradi neexistuje
  • Rozdílový seznam: [ 1,2,3 | T1]-T1, [4,5,6|T2]-T2
 Spojení rozdílových seznamů: conc(A-B, B-C, A-C). V konstantnim case.


Užitečné Haskellovské funkce

  • zip, forldl, foldr, sum

Užitečné progr. techniky z learnyouhaskell.com

  • case lze pouzit prakticky kdekoli
 describeList :: [a] -> String
 describeList xs = "The list is " ++ case xs of [] -> "empty."  
                                                [x] -> "a singleton list."   
                                                xs -> "a longer list."  
  • If you want to get an element out of a list by index, use !!. The indices start at 0.
 ghci> "Steve Buscemi" !! 6  
 'B'  
 ghci> [9.4,33.2,96.2,11.2,23.25] !! 1  
 
  • Haskell umí porovnávat n-tice: (1,2,3) <= (3,4,5) vrátí TRUE.


Ústní

PROLOG

- Tvar programu v Prologu a jeho interpretace

- Deklarativní a operační sémantika programu v Prologu

- Operátor řezu

- Negace

- Práce se seznamy

- Reprezentace datových struktur (např. grafy, stromy, rozdílové seznamy)

- Edinbugrský model vstupu a výstupu.

- Definování a použití operátorů

- Predikáty pro řízení databáze (assert,...)

- Predikáty grupování termů (bagof, setof) a jejich užití

- Neúplně definované datové struktury, rozdílové seznamy

- Efektivita programů v Prologu

HASKELL

- Typy v Haskellu, typová specifikace funkce

- Základní způsoby definování výrazů,

- Sémantika "mečování" parametrů, as patterns ( @s ), žolíky ( _ ), lazy-parametry ( ˜x ),

- Lazy vyhodnocování, "nekonečné" termy.

- Lambda abstrakce (tj. lambda funkce)

    Vyhneme se definovaní nových funkcí, pokud danou funkci jinak nepotřebujeme.
    
    pr. inc x   = x+1      => lambda funkce =>  \x -> x+1   
    

- Používání jmen funkcí jako operátorů a naopak, specializace operátorů { (např. (x+) }.

- Definování priority a asociativity infixových operátorů.

    pr. infixr 5 ++          -- prava asociativita, 0 nejmensi 9 nejvetsi;
    pr. infixl 2 <operator>  -- leva asociativita
    pr. infix                -- zadna asociativita
    
    
    main = print (1 +++ 2 *** 3)
    main2 = print (1 ++++ 2 ++++ 3)
   
    infixr  6 +++
    infixl  5 ***,///
    infixl  6 ++++
       
    (+++) :: Int -> Int -> Int
    a +++ b = a + 2*b
   
    (++++) :: Int -> Int -> Int
    a ++++ b = a - b
       
    (***) :: Int -> Int -> Int
    a *** b = a - 4*b
   
    (///) :: Int -> Int -> Int
    a /// b = 2*a - 3*b
    
    

- Třídy, podtřídy, instance.

- Pole v Haskellu.