name = 'divide/3' slug = 'Razdeli dani seznam na dva podseznama (približno) enake velikosti' description = '''\
divide(L, L1, L2)
: seznam L1
vsebuje elemente na lihih pozicijah v seznamu L
, seznam L2
pa elemente na sodih pozicijah v L
. Rešuj brez uporabe aritmetike.
?- divide([a,b,c,d,e,f,g], X, Y). X = [a,c,e,g], Y = [b,d,f]. ?- divide([a,b,c,d,e,f], X, Y). X = [a,c,e], Y = [b,d,f].''' plan = ['''\
Saj veš kako je šlo v osnovni šoli: prvi, drugi, prvi, ...
''', '''\Znaš vzeti dva elementa z začetka seznama? Vzorec je [H1,H2|T]
.
Vzameš dva elementa z začetka, preostanek rekurzivno razdeliš in to, kar vrne rekurzija, primerno dopolniš s prej odvzetima elementoma. S tem, ko si vzel dva elementa z začetka, si problem zmanjšal.
''', '''\Če predpostavim, da rekurzija razdeli rep T
na podseznama L1
in L2
ter ob vračanju v L1
na začetek dodam H1
in v L2
na začetek
dodam H2
, potem sem razdelil začetni seznam, ki je oblike [H1,H2|T]
.
Operator ==
je strožji od operatorja =
v smislu, da je za slednjega dovolj,
da elementa lahko naredi enaka (unifikacija).
Seveda pa lahko nalogo rešiš brez obeh omenjenih operatorjev, spomni se, da lahko unifikacijo narediš implicitno že kar v argumentih predikata (glavi stavka).
''', 'eq_instead_of_equ_markup': '''\Morda bi bil bolj primeren operator za unifikacijo (=)?
''', 'base_case': '''\Si pomislil na robni pogoj? Kaj je najbolj enostaven primer? Kaj, če je seznam prazen?
''', 'base_case_arbitrary': '''\Kako je lahko rezultat delitve seznama poljuben seznam oz. karkoli?
Če je tvoj robni pogoj v stilu divide([], _, _)
ali divide([X], [X|_], ...)
,
ga še enkrat premisli: kaj je rezultat delitve, kaj vračaš? Robni pogoj je vedno dokončno specificirana
rešitev, tu načeloma ni neznank (_
ali spremenljivk brez prirejenih vrednosti) v tem kar se vrača.
Rekurzija se ne konča vedno uspešno. Sta morda dva različna primera kako se lahko izteče? Saj veš, sodo in liho ;) Je morda potreben še kakšen robni pogoj? Poskusi naslednja klica, eden bo uspešen, drugi pa ne:
?- divide([a,b,c], L1, L2).
?- divide([a,b,c,d], L1, L2).
Uporabljaš conc/3
? Pri tej nalogi to ni najboljša ideja, ker conc/3
deli "v kosih",
težko boš prišel do posameznih elementov. Poskusi raje brez.
Ne vsiljuj rekurziji kaj naj vrne, prepusti se ji. To je tisti del, ko narediš predpostavko, če je ta izpolnjena, potem bo tvoje pravilo delovalo za večji primer.
Je tvoj rekurzivni klic oblike divide(T, [H1|...], [H2|...])
? S tem vsiljuješ rekurziji
da mora H1
in H2
dodaj izven rekurzivnega klica.
Robni primeri delujejo. Kaj pa rekurzivni, splošni, primer?
''', 'predicate_always_false': '''\Vse kaže, da tvoj predikat vedno vrne "false". Si mu dal pravilno ime, si se morda pri imenu zatipkal?
Če je ime pravilno, se morda splača preveriti tudi, če se nisi zatipkal kje drugje, je morda kakšna pika namesto vejice ali obratno, morda kakšna spremenljivka z malo začetnico?
Možno je seveda tudi, da so tvoji pogoji prestrogi ali celo nemogoči (kot bi bila npr. zahteva,
da je X
hkrati starš in sestra od Y
ali kaj podobno zlobnega).
Je morda na delu potencialno neskončna rekurzija? Kako se bo ustavila?
Morda pa je kriv tudi manjkajoč, neustrezen ali preprosto nekompatibilen (s splošnim primerom) robni pogoj?
''', }