Is it possible to go back to a repeat
in Prolog without calling the predicate and without making a new predicate?
I have the following code
test :- nl,
write('Welcome.'),nl,
repeat, write('Print this message again? (yes/no)'),nl,
read(Ans),nl,
(
Ans == yes -> write('You selected yes.'), nl
;
write('You selected no.')
).
The current output I get is
Welcome.
Print this message again? (yes/no)
yes.
You selected yes.
true.
Program end.
The output I want is
Welcome.
Print this message again? (yes/no)
yes.
You selected yes.
Print this message again? (yes/no)
no.
Program ends.
The easy output way I want to avoid (I don't want this output. I don't want it shows Welcome multiple of times):
Welcome.
Print this message again? (yes/no)
yes.
Welcome.
You selected yes.
Print this message again? (yes/no)
no.
Program ends.
repeat/0
is simply defined as:
repeat.
repeat :- repeat.
Or, equivalently:
repeat :- true ; repeat.
In order to repeat, you need to backtrack up to the repeat
call by failing, either explicitely with fail
or through another failing predicate (see example in the above link).
...
repeat,
...,
fail.
Once you want to exit the repeating pattern, you can (and should) cut !
the decision tree so that you don't have a dangling repeat
choice point.
If you don't, the interpreter will still have the possibility to backtrack to repeat
later.
NB: the rules for !/0
can be found here.
For you example specifically, that means (btw, I use writeln
):
test :-
nl,
writeln('Welcome.'),
repeat,
writeln('Print this message again? (yes/no)'),
read(Ans),nl,
(Ans == yes ->
writeln('You selected yes.'),
fail % backtrack to repeat
; writeln('You selected no.'),
! % cut, we won't backtrack to repeat anymore
).
Notice that OP used atoms, whereas strings are sufficient. Indeed, atoms (single-quotes) are hashed and prefered for symbolic reasoning, whereas strings (double-quotes) are not interned and are more adequate for displaying messages.
In the same spirit, when reading, I'd rather use read_string(end_of_line,_,S)
, which reads up-to the end of line and returns a string. With read/1
, I had to close the input stream with Ctrl+D, which was annoying.
Also, we can get rid of ->
completely:
test :-
nl,
writeln("Welcome."),
repeat,
writeln("Print this message again? (yes/no)"),
read_string(end_of_line,_,Ans),
nl,
write("You selected "),
write(Ans),
writeln("."),
Ans == "no", % Otherwise, repeat
!.
Removing ->
might be controversial seeing how other people argue about having more cases. Here is the rationale: since the original question seems to be an homework about repeat
, the parts about handling yes
, no
and bad inputs explicitely seems to be underspecified, and frankly, not really relevant. I kept the original semantics and merged the yes
and bad-input cases: after all, what happens when user says yes
? we repeat, exactly as when a user types an unexpected input. The only case where we do not repeat
is when Ans == no
.
Now, if we want to change the behavior of the original code in order to explicitely check for all possible kind of inputs, here is an attempt:
test :-
nl,
writeln("Welcome."),
repeat,
writeln("Print this message again? (yes/no)"),
read_string(end_of_line,_,Ans),
nl,
(memberchk(Ans,["yes","no"]) ->
write("You selected "),
write(Ans),
writeln("."),
Ans == "no",
!
; writeln("Bad input" : Ans),
fail).
Why you don't try to do:
test :-
nl, write('Welcome.'),
nl, test_internal.
test_internal :-
write('Print this message again? (yes/no)'), nl,
read(Ans), nl,
( Ans == yes
-> write('You selected yes.'), nl, test_internal
; Ans == no, write('You selected no.'), !
; test_internal
).
EDIT
If you cannot separate the predicate in two, another solution (use the coredump one's) could be:
test :-
nl, write('Welcome.'),
repeat, nl,
write('Print this message again? (yes/no)'), nl,
read(Ans), nl,
( Ans == yes
-> write('You selected yes.'), fail
; Ans == no, write('You selected no.'), !
; fail
).
To further increase readability, (->)/2
(if-then-ELSE-FAIL) can be used (c.f. SWI-Prolog manual section on control predicates).
Also, a different layout of the if-then-else cascade can help.
test :-
nl, write('Welcome.'),
repeat, nl,
write('Print this message again? (yes/no)'), nl,
read(Ans), nl,
( Ans == yes -> write('You selected yes.'), fail
; Ans == no -> write('You selected no.'), !
).
Note that the use of if-then-ELSE-FAIL is not strictly necessary---conjunction could be used. Using it, however, makes it easy to add code handling additional cases (maybe
, i_dont_know
, i_m_afraid
, i_gotta_go
) in the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With