name = 'del/3' slug = 'Zbriši element iz seznama' description = '''\
del(X, L1, L2)
: seznam L2
dobimo iz seznama L1
tako da zbrišemo element X
.
?- del(1, [1,2,3], L). L = [2,3]. ?- del(2, [1,2,3,2,5], L). L = [1,3,2,5] ; L = [1,2,3,5]. ?- del(X, [1,2,3], L). X = 1, L = [2,3] ; X = 2, L = [1,3] ; X = 3, L = [1,2].''' plan = ['''
V bistvu je ta naloga precej podobna nalogi memb/2
, le da tokrat iskani element tudi zbrišemo.
Kje se lahko skriva iskani element X
, da ga zbrišemo? Spomni se, da ima seznam dva dela, glavo in rep.
Torej sta možnosti dve!
Kaj je najenostavnejša smiselna možnost? Morda brisanje prvega elementa?
''', '''\Kako brišem nekje iz repa? Seznam razbijem na glavo in rep, rekurzivno (problem je za en element manjši!) brišem iz repa in ob vračanju iz rekurzije ne pozabim na prej "odtrgano" glavo.
''', '''\Rekurzivni korak: če predpostavim, da je NewTail
rep z že izbrisanim elementom X
,
potem je [H|NewTail]
celoten seznam z izbrisanim elementom X
.
Operator ==
je strožji od operatorja =
v smislu, da je za slednjega dovolj,
da elementa lahko naredi enaka (unifikacija). Morda z uporabo =
narediš predikat
del/3
delujoč tudi v kakšni drugi smeri.
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, kateri element v seznamu najlažje zbrišeš?
''', 'recursive_case': '''\Robni primer deluje. 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?
''', 'del_from_empty_list_success': '''\Iz praznega seznama ne moreš uspešno zbrisati nobenega elementa!
Če brišem iz praznega seznama, ne dobim praznega seznama ali tudi karkoli drugega kot rezultat, ampak naj prolog preprosto ne uspe -- tega niti ne potrebuješ pisati kot pravilo, ker je to prologov privzet odgovor: saj veš, da z največjim veseljem reče "no"! :)
Če je to tvoj robni pogoj, ga še enkrat premisli: na katerem mestu je najlažje zbrisati element v seznamu?
''', 'lost_heads': '''\Element je zbrisan, ampak prav tako so tudi vsi elementi pred njim, kajne? Si pozabil dati glavo nazaj na začetek seznama, ko se vračaš iz rekurzije?
Poskusi postaviti naslednje vprašanje prologu:
?- del(d, [a,b,c,d,e,f,g], L).
Si morda pozabil (copy/paste?) in uporabil [X|T]
namesto bolj splošnega
[H|T]
v rekurzivnem primeru?
Od spodnjih dveh vprašanj prologu prvo deluje, drugo pa ne.
?- del(d, [d,d,d,d,e,f,g], L).
?- del(d, [a,b,c,d,e,f,g], L).
Zanimivost: operaciji vstavljanja in brisanja iz seznama sta si ravno nasprotni. Če se malce poigraš z
argumenti, lahko del/3
rešiš kar z insert/3
.
Logično velja naslednje: če zbrišem X
iz seznama BigList
in dobim kot rezultat
seznam SmallList
je isto kot če vstavim X
v seznam SmallList
in dobim
kot rezultat seznam BigList
. ;)