{{Předmět|Neprocedurální programování|Rudolf Kryl|AIL076}}

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 <Rudolf%20Kryl> 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 <Algoritmy%20a%20datové%20struktury> alebo z <Automaty%20a%20gramatiky>. 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

  1. 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

  1. 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](1%2C4%2C6%2C7%5D%2C%5B2%2C3%2C5))" , 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](1%2C%204%5D%2C%20%5B2%2C%203)

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](1%2C4%5D%2C%5B2%2C3%5D%2C%5B3%2C2%5D%2C%5B4%2C1), 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ů

  1. 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

  1. Čí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

  1. 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"

  1. 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

  1. 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

  1. Čí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

  1. 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
  1. 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      
  1. 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
  1. Ří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

  1. 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.

  1. 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
  1. 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/

  1. 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

  2. Ú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                  
  1. 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)   
  1. 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

  • http://prgs.xf.cz/ - nějaké řešené úložky

  • http://www.haskell.org/haskellwiki/H-99:_Ninety-Nine_Haskell_Problems - rešené problémy

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 = 2a - 3b

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

  • Pole v Haskellu.

Category:Informatika