如何解析序幕中的列表,直到不失败

时间:2019-02-27 05:04:55

标签: prolog

我正在尝试解析列表,例如: [1,3,3,2,2,7,2,9]

假设我有以下代码,其中isTwo将打印No,如果不是2则失败。如果不是两个,我希望失败。如果为2,它将打印Yes并成功。

isTwo(2) :- 
    write('Yes'), !.

isTwo(_) :- 
    write('No'), fail.

我的问题是我在定义迭代器的过程中遇到了一些麻烦,我想解析整个列表以找到第一个成功的对象。

iter([]).

iter([Head|Tail]) :-
    isTwo(Head),
    iter(Tail).

这很好,但是一旦发现故障,它将停止解析列表的其余部分。如果我们返回原始列表[1,3,3,2,2,7,2,9],则会得到false作为输出。

我的问题是:如何继续在序言中解析,同时仍然确保isTwo(_)(不等于2)仍然失败,因此我可以得到类似{ {1}}在这种情况下。最好不要使用if-then-else子句。

这可能有帮助:

预期输出:NoNoNoYesYesNoYesNo

观察到的输出:NoNoNoYesYesNoYesNo

3 个答案:

答案 0 :(得分:2)

一种简单的解决方案是在iter中使用第二个变量,该变量将帮助您理解是否找到了不同于2的数字:

isTwo(2, X, X) :- 
    write('Yes').

isTwo(_, _, 0) :- 
    write('No').


iter([],  0):- fail,!.
iter([],  1).
iter([Head|Tail], X) :-
    isTwo(Head, X, Y),
    iter(Tail, Y).


iter(L) :- iter(L, 1).

答案 1 :(得分:1)

如果您想要更简洁的解决方案,可以使用maplist/2进行以下操作:

isTwo(2) :- write('Yes'), !.
isTwo(_):- write('No').

test(L):-
    maplist(isTwo,L).

?- test([1,3,3,2,2,7,2,9]).
NoNoNoYesYesNoYesNo
true

test/1不是强制性的,我只是为了清楚起见添加了它...

答案 2 :(得分:0)

其他答案都很好,但是随意混合的副作用让我有点紧张。另外,如果您要“解析”的列表有一个自由变量(它会静默变成2),您就会遇到问题。

为什么不这样:

is_two(X) :- X == 2, !, format("Yes").
is_two(X) :- X \== 2, !, format("No").

然后:

?- forall(member(X, [1,3,3,2,2,7,2,9]), is_two(X)).
NoNoNoYesYesNoYesNo
true.

?- forall(member(X, [Y, 2, Z]), is_two(X)).
NoYesNo
true.

使用==\==进行比较而不统一。使用forallmember来表明您这样做是为了产生副作用。遍历列表(或使用maplist)有点欺骗。

forall只是使用否定(而不是削减和失败)进行失败驱动循环的一种更清晰的方法。上面的查询与:

?- \+ ( member(X, [1,3,3,2,2,7,2,9]), \+ is_two(X) ).