How to go back to "repeat" in Prolog?

Repeat

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.

Example

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

Other remarks

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

Edit: alternative using if-then and different layout

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.

Tags:

Prolog

Repeat