如何在prolog中解析列表,直到不失败

如何在prolog中解析列表,直到不失败,prolog,Prolog,我正在尝试解析列表,例如: [1,3,3,2,2,7,2,9] 假设我有以下代码,其中isTwo将打印No,如果不是两个,则会失败。如果不是两个,我希望它失败。如果是2,它将打印Yes并成功 isTwo(2) :- write('Yes'), !. isTwo(_) :- write('No'), fail. 我的问题是,我已经定义了一个迭代器,我想解析一个完整的列表来找到第一个成功 iter([]). iter([Head|Tail]) :- isTwo(He

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

假设我有以下代码,其中isTwo将打印
No
,如果不是两个,则会失败。如果不是两个,我希望它失败。如果是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

我这里的问题是:如何在prolog中继续解析,同时仍然确保
isTwo()
(它不等于2)仍然会失败,这样在这种情况下我就可以得到
nononoyesnoyesno
之类的输出。最好不使用if-then-else子句

这可能有助于:

预期输出:
nononoyesnoyesno


观察到的结果:
No
一个简单的解决方案是在
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).

如果您想要更简洁的解决方案,可以使用
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
静默变量),则会出现问题

为什么不这样:

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.
使用
=
\=
进行比较,但不统一。使用
forall
member
明确说明您这样做是为了产生副作用。遍历列表(或使用
maplist
)有点欺骗

forall
只是一种更清晰的方法,可以使用否定而不是切割和失败来执行故障驱动的循环。上述查询与以下内容相同:

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

只要摆脱
失败
。你让这件事对你自己来说比你需要的更难。