如何按顺序对列表中的每个元素执行操作?
基于这两个资源:
我想我总能依赖:
foreach(member(X, [1,2]), write(X)).
这是确定性的,我可以在我自己的谓词中包装成员/ 2谓词,并且仍然总是按顺序迭代吗?
答案 0 :(得分:20)
是的,但你必须担心你的谓词失败。如果可以,则不会处理列表中的其余元素,因为它会生成连接而不是故障驱动的循环。
我会更热衷于使用maplist/2
,因为我认为它比foreach/2
更广泛使用,但我之前也没有看过这个选项。 :)
编辑:让我们讨论一下故障驱动循环的含义。
Prolog中有两种原始迭代方法:递归和失败驱动的循环。假设我想打印出列表中的每个项目。递归方法看起来像这样:
print_all([]).
print_all([X|Rest]) :- write(X), nl, print_all(Rest).
所以给定一个像[1,2,3]
这样的列表,这将会像这样扩展:
print_all([1,2,3])
write(1), nl, print_all([2,3])
write(1), nl, write(2), nl, print_all([3])
write(1), nl, write(2), nl, write(3), nl, print_all([])
write(1), nl, write(2), nl, write(3), nl.
这是member/2
通常实施的方式:
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
所以你可以看到递归方法非常简单和通用。
另一种简单但有点皱眉的方法是模拟未能使用回溯机制。这称为故障驱动循环,如下所示:
print_all(List) :- member(X, List), write(X), nl, fail.
print_all(_).
当您运行此版本的print_all/1
时,会发生什么比简单扩展稍微复杂一点。
print_all([1,2,3])
member([1,2,3], 1)
write(1), nl
fail
retry member([1,2,3], 2)
write(2), nl
fail
retry member([1,2,3], 3)
write(3), nl
fail
retry print_all(_)
true
口头上,fail
迫使Prolog备份到它所做的最后一个选择点并尝试使用下一个解决方案。好吧,write/1
和nl/0
不会产生选择点,因为它们只有一个解决方案,但member/2
确实有多个解决方案 - 每个项目一个名单。所以Prolog从列表中取出每个项目并打印出来。最后,当member/2
用完解决方案时,Prolog会备份到前一个选择点,这是print_all/1
谓词的第二个主体,它始终成功。所以输出看起来一样。我认为现在人们通常不喜欢使用故障驱动的循环,但是我不能很好地理解这些论点以便有用地鹦鹉学舌。
可以帮助您查看正在发生的事情的一件事是使用trace
谓词并逐步扩展两个版本,看看您是否能够理解这些差异。我的上述注释完全弥补了这个答案,可能不太清楚。
回顾我最初写的内容和你的实际问题:
foreach
将是确定性的member
将始终按顺序迭代,因为列表的定义方式必须依次访问每个项目此外,这些日子至少在S.O.你会得到很多人告诉你使用maplist
及其类似的东西,所以它可能不仅仅是起作用,而且也是一个好主意。