如何在Prolog中重新“重复”?

时间:2015-04-24 21:00:22

标签: prolog repeat

是否可以在不调用函数(谓词)且不创建新函数的情况下返回Prolog中重复?

我有以下代码

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

我得到的当前输出是

Welcome.
Print this message again? (yes/no)
yes.
You selected yes.
true.

节目结束。

我想要的输出是

Welcome.
Print this message again? (yes/no)
yes.
You selected yes.
Print this message again? (yes/no)
no.

计划结束。

我想避免的简单输出方式(我不想要这个输出。我不希望它显示多次欢迎):

Welcome.
Print this message again? (yes/no)
yes.

Welcome.
You selected yes.
Print this message again? (yes/no)
no.

计划结束。

2 个答案:

答案 0 :(得分:6)

重复

repeat/0simply defined as

repeat.
repeat :- repeat.

或等同地:

repeat :- true ; repeat.

要重复,您需要通过失败来回溯repeat调用,或者使用fail或通过其他失败的谓词(请参阅上面的链接中的示例)。

...
repeat,
...,
fail.

一旦你想退出重复模式,你可以(并且应该)剪切!决策树,这样你就没有悬空的repeat选择点。 如果不这样做,解释器仍然可以在以后回溯到repeat

注意:!/0的规则可以是found here

实施例

具体来说,这意味着(顺便说一句,我使用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
  ).

其他评论

请注意,OP使用原子,而字符串就足够了。 实际上,原子(单引号)是经过哈希处理的,并且优先用于符号推理,而字符串(双引号)不是实例,而是更适合显示消息。

本着同样的精神,在阅读时,我宁愿使用read_string(end_of_line,_,S),它会读到行尾并返回一个字符串。使用read/1,我必须使用 Ctrl + D 关闭输入流,这很烦人。

另外,我们可以完全摆脱->

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

删除->可能会引起争议,看看其他人如何争论更多案件。以下是基本原理:由于原始问题似乎是关于repeat的一项功课,因此有关处理yesno和错误输入的部分明显不明确,坦率地说,并非如此相关。我保留了原始语义并合并了yes和错误输入的案例:毕竟,当用户说yes时会发生什么?我们重复,就像用户键入意外输入时一样。我们不repeat的唯一情况是Ans == no

现在,如果我们想要更改原始代码的行为以便明确检查所有可能类型的输入,这是一次尝试:

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

答案 1 :(得分:3)

为什么你不尝试这样做:

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

修改

如果你不能将谓词分成两个,另一个解决方案(使用 coredump 一个)可能是:

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

编辑:使用if-then和不同布局的替代

为了进一步提高可读性,可以使用(->)/2(if-then-ELSE-FAIL)(c.f。SWI-Prolog manual section on control predicates)。 此外,if-then-else级联的不同布局可以提供帮助。

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.'),  !
     ).

请注意,使用if-then-ELSE-FAIL并非绝对必要---可以使用连接。但是,使用它可以在将来轻松添加处理其他案例(maybei_dont_knowi_m_afraidi_gotta_go)的代码。