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