summaryrefslogtreecommitdiff
path: root/prolog/problems/lists
diff options
context:
space:
mode:
authorAleksander Sadikov <aleksander.sadikov@fri.uni-lj.si>2016-03-11 19:58:03 +0100
committerAleksander Sadikov <aleksander.sadikov@fri.uni-lj.si>2016-03-11 19:58:03 +0100
commit8a9f429b6c126e00af8d5f2650d6c4d705917b6a (patch)
tree4a1002a423b7802b05b693445372c895c5d531f7 /prolog/problems/lists
parent053be5a6671a765b4adaa5f54f90504007ecac30 (diff)
Plans for conc/3 added. Order of hints in dup/2 corrected.
Diffstat (limited to 'prolog/problems/lists')
-rw-r--r--prolog/problems/lists/conc_3/common.py43
-rw-r--r--prolog/problems/lists/conc_3/sl.py61
-rw-r--r--prolog/problems/lists/dup_2/common.py17
3 files changed, 110 insertions, 11 deletions
diff --git a/prolog/problems/lists/conc_3/common.py b/prolog/problems/lists/conc_3/common.py
index 67f9481..ce81bbe 100644
--- a/prolog/problems/lists/conc_3/common.py
+++ b/prolog/problems/lists/conc_3/common.py
@@ -18,6 +18,15 @@ conc([H|T], L2, [H|L]) :-
conc(T, L2, L).
'''
+hint_type = {
+ 'eq_instead_of_equ_markup': HintPopup('eq_instead_of_equ_markup'),
+ 'eq_instead_of_equ': Hint('eq_instead_of_equ'),
+ 'predicate_always_false': Hint('predicate_always_false'),
+ 'base_case': Hint('base_case'),
+ 'timeout': Hint('timeout'),
+ 'final_hint': Hint('final_hint'),
+}
+
test_cases = [
('conc([j, b], [l], X)',
[{'X': '[j, b, l]'}]),
@@ -46,8 +55,40 @@ def test(code, aux_code):
prolog.engine.destroy(engine_id)
hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_cases)}}]
+ if n_correct == len(test_cases):
+ hints += [{'id': 'final_hint'}]
return n_correct, len(test_cases), hints
def hint(code, aux_code):
- # TODO
+ tokens = prolog.util.tokenize(code)
+
+ try:
+ engine_id, output = prolog.engine.create(code=code+aux_code, timeout=1.0)
+
+ # strict equality testing instead of simple matching
+ # this is usually (but not necessarily) wrong
+ targets = [prolog.util.Token('EQ', '==')]
+ marks = [(t.pos, t.pos + len(t.val)) for t in tokens if t in targets]
+ if marks:
+ return [{'id': 'eq_instead_of_equ_markup', 'start': m[0], 'end': m[1]} for m in marks] + \
+ [{'id': 'eq_instead_of_equ'}]
+
+ # recursion is getting bigger and bigger
+
+
+ # missing/failed base case
+ if not prolog.engine.ask_truthTO(engine_id, 'memb(qq, [qq,_,_,_])'):
+ return [{'id': 'base_case'}]
+
+ # target predicate seems to always be false
+ if not prolog.engine.ask_truth(engine_id, 'memb(_, [_,_,_,_,_,_])'):
+ return [{'id': 'predicate_always_false'}]
+
+ except socket.timeout as ex:
+ return [{'id': 'timeout'}]
+
+ finally:
+ if engine_id:
+ prolog.engine.destroy(engine_id)
+
return []
diff --git a/prolog/problems/lists/conc_3/sl.py b/prolog/problems/lists/conc_3/sl.py
index 0d5af8f..779b3ed 100644
--- a/prolog/problems/lists/conc_3/sl.py
+++ b/prolog/problems/lists/conc_3/sl.py
@@ -12,4 +12,63 @@ description = '''\
X = [1,2,3].
</pre>'''
-hint = {}
+plan = ['''\
+<p>Začnimo z enostavnim vprašanjem: kaj bi bil rezultat, če konkateniram prazen seznam in seznam <code>L2</code>?</p>
+''', '''\
+<p>Sedaj recimo, da ima prvi seznam točno en element. Začasno ga vzemimo stran, ostali smo s praznim prvim seznamom.
+Kaj ni ta situacija podobna oni od prej? Seveda, problem smo zmanjšali za en element in ga tako prevedli na manjši
+problem! Uporabimo rekurzijo za rešitev tega manjšega problema. A na koncu ne pozabimo rezultat rekurzije ustrezno
+dopolniti s prej odvzetim prvim elementom...</p>
+''', '''\
+<p>Deklarativno/logično razmišljanje: Recimo, da ima prvi seznam <code>L1</code> glavo <code>H</code> in rep
+<code>T</code>. Če je (rekurzivni) rezultat konkatenacije <code>T</code> in <code>L2</code> nek seznam
+<code>L3</code> in če seznamu <code>L3</code> na začetek dodam element <code>H</code>, kaj s tem dobim?
+Konkatenacijo seznamov <code>L1</code> in <code>L2</code>!</p>
+''']
+
+hint = {
+ 'eq_instead_of_equ': '''\
+<p>Operator <code>==</code> je strožji od operatorja <code>=</code> v smislu, da je za slednjega dovolj,
+da elementa lahko naredi enaka (unifikacija). Morda z uporabo <code>=</code> narediš predikat
+<code>conc/3</code> delujoč tudi v kakšni drugi smeri.</p>
+<p>Seveda pa lahko nalogo rešiš brez obeh omenjenih operatorjev, spomni se, da lahko unifikacijo narediš
+implicitno že kar v argumentih predikata (glavi stavka).</p>
+''',
+
+ 'eq_instead_of_equ_markup': '''\
+<p>Morda bi bil bolj primeren operator za unifikacijo (=)?</p>
+''',
+
+ 'base_case': '''\
+<p>Si pomislil na robni pogoj? Kaj je najbolj enostaven primer?
+Kaj bi bil, recimo, rezultat, če je prvi seznam kar prazen?</p>
+''',
+
+ 'predicate_always_false': '''\
+<p>Vse kaže, da tvoj predikat vedno vrne "false". Si mu dal pravilno ime, si se morda pri imenu zatipkal?</p>
+<p>Č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?</p>
+<p>Možno je seveda tudi, da so tvoji pogoji prestrogi ali celo nemogoči (kot bi bila npr. zahteva,
+da je <code>X</code> hkrati starš in sestra od <code>Y</code> ali kaj podobno zlobnega).</p>
+''',
+
+ 'timeout': '''\
+<p>Je morda na delu potencialno neskončna rekurzija? Kako se bo ustavila?</p>
+<p>Morda pa je kriv tudi manjkajoč, neustrezen ali preprosto nekompatibilen (s splošnim primerom) robni pogoj?
+Morda npr. v rekurziji zmanjšuješ prvi seznam, ustaviš pa se pri praznem drugem seznamu (ali obratno)?</p>
+''',
+
+ 'final_hint': '''\
+<p>Predikat <code>conc/3</code> bomo večinoma uporabljali za vse drugo kot samo za konkatenacijo dveh seznamov.
+Med drugim je uporaben "v obratni smeri" za delitev seznama na dva dela, poskusi naslednja vprašanja:</p>
+<p><code>?- conc(L1, L2, [a,b,c,d]).</code></p>
+<p><code>?- conc([X,Y], L2, [a,b,c,d,e,f]).</code></p>
+<p>Si opazil, da je drugo vprašanje v bistvu vrnilo prva dva elementa iz seznama <code>[a,b,c,d,e,f]</code>?</p>
+<p>Nadalje je <code>conc/3</code> uporaben za iskanje vzorcev v seznamu, npr. takole:</p>
+<p><code>?- conc(_, [X,X|_], [a,b,c,c,d,e,f,f,g,h,h]).</code></p>
+<p>Tako je, to vprašanje najde vse možnosti, kjer se dva elementa ponovita drug za drugim v seznamu (vzorec X,X).
+V bistvu smo rekli "nekaj poljubnih elementov (lahko tudi nič) je spredaj, potem sta dva enaka, potem pa spet nekaj
+poljubnih elementov (lahko nič) zadaj."
+Še veliko drugih koristi bo od <code>conc/3</code>, jih boš že še sproti spoznal.</p>
+''',
+}
diff --git a/prolog/problems/lists/dup_2/common.py b/prolog/problems/lists/dup_2/common.py
index 0ed3914..6e31690 100644
--- a/prolog/problems/lists/dup_2/common.py
+++ b/prolog/problems/lists/dup_2/common.py
@@ -77,15 +77,6 @@ def hint(code, aux_code):
# recursion is getting bigger and bigger
- # forcing result onto recursion; case of dup(T, [H,H|L]) or dup(T, [H,H|T])
- if not prolog.engine.ask_truthTO(engine_id, 'dup([a,b,c,d,e], _)') and \
- (prolog.engine.ask_truth(engine_id,
- 'asserta( dup([b], [a,a,yowza]) ), dup([a,b], [yowza]), retract( dup([b], [a,a,yowza]) )') or \
- prolog.engine.ask_truth(engine_id,
- 'asserta( dup([b], [a,a,b]) ), dup([a,b], [b]), retract( dup([b], [a,a,b]) )')):
- return [{'id': 'forcing_result_onto_recursion'}]
-
-
# base case succeeds with arbitrary result
if prolog.engine.ask_truthTO(engine_id, 'dup([], L), var(L)'):
return [{'id': 'base_case_arbitrary'}]
@@ -94,6 +85,14 @@ def hint(code, aux_code):
if prolog.engine.ask_truthTO(engine_id, 'dup([q], [q,q]), \+ dup([], [])'):
return [{'id': 'base_case_missing_[]'}]
+ # forcing result onto recursion; case of dup(T, [H,H|L]) or dup(T, [H,H|T])
+ if not prolog.engine.ask_truthTO(engine_id, 'dup([a,b,c,d,e], _)') and \
+ (prolog.engine.ask_truth(engine_id,
+ 'asserta( dup([b], [a,a,yowza]) ), dup([a,b], [yowza]), retract( dup([b], [a,a,yowza]) )') or \
+ prolog.engine.ask_truth(engine_id,
+ 'asserta( dup([b], [a,a,b]) ), dup([a,b], [b]), retract( dup([b], [a,a,b]) )')):
+ return [{'id': 'forcing_result_onto_recursion'}]
+
# missing/failed base case
if not prolog.engine.ask_one(engine_id, 'dup([], [])'):
return [{'id': 'base_case'}]