对 swi-prolog 等中的每个列表元素执行操作
如何按顺序对列表中的每个元素进行操作?
基于这两个资源:
- http://www.swi-prolog .org/pldoc/doc/swi/library/lists.pl
- http://www.swi-prolog.org/pldoc/doc_for?object=foreach/2
我想我总是可以依赖:
foreach(member(X, [1 ,2]), write(X))。
这是确定性的吗?我可以按照自己的意愿将 member/2 谓词包装在自己的谓词中,并且仍然始终按顺序迭代吗?
How do I do an operation for each element of a list, in order?
Based on these two resources:
- http://www.swi-prolog.org/pldoc/doc/swi/library/lists.pl
- http://www.swi-prolog.org/pldoc/doc_for?object=foreach/2
I imagine I can always rely on:
foreach(member(X, [1,2]), write(X)).
Is that deterministic and can I wrap the member/2 predicate as I please in my own predicates and still always iterate in order?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,但是你必须担心你的谓词失败。如果可以,列表中的剩余元素将不会被处理,因为它产生一个合取而不是一个故障驱动的循环。
我更愿意使用
maplist/2
因为我认为它比foreach/2
使用更广泛,但我以前也没有见过这个选项。 :)编辑:让我们讨论一下故障驱动循环的含义。
Prolog中有两种原始迭代方法:递归和故障驱动循环。假设我想打印列表中的每个项目。递归方法将如下所示:
因此,给定一个像
[1,2,3]
这样的列表,它将像这样展开:这就是
member/2
> 通常会被实现:所以你可以看到递归方法非常简单和通用。
另一种简单但有些不受欢迎的方法是模拟回溯机制的失败。这称为故障驱动循环,如下所示:
当您运行此版本的
print_all/1
时,发生的情况比简单扩展稍微复杂一些。从口头上来说,
失败
迫使 Prolog 备份到它所做的最后一个选择点,并尝试使用下一个解决方案。好吧,write/1
和nl/0
不会产生选择点,因为它们只有一个解决方案,但是member/2
确实有多种解决方案——列表中的每一项都有一个解决方案。因此 Prolog 从列表中取出每个项目并打印它。最后,当member/2
用完解决方案时,Prolog会备份到之前的选择点,即print_all/1
谓词的第二个主体,它总是成功。所以输出看起来是一样的。我认为现在的人们通常不喜欢使用故障驱动循环,但我对这些论点的理解还不够深入,无法有效地重复它们。可以帮助您了解正在发生的事情的一件事是使用
trace
谓词并逐步完成两个版本的扩展,看看您是否可以理解差异。我上面的注释完全是为了这个答案而弥补的,可能不太清楚。回顾一下我最初写的内容和您的实际问题:
foreach
将是确定性的member
将始终按顺序迭代,因为列表的定义方式您必须依次访问每个项目此外,至少现在,你会得到很多人告诉你使用
maplist
及其同类,所以它可能不仅可以工作,而且是一个好主意。Yes, but you have to worry about your predicate failing. If it can, the remaining elements in the list will not be processed because it produces a conjunction rather than a failure-driven loop.
I would be more keen to use
maplist/2
since I think it is more widely used thanforeach/2
but I also hadn't seen this option before. :)Edit: Let's discuss what I mean about failure-driven loops.
There are two primitive iteration methods in Prolog: recursion and failure-driven loops. Say I want to print out every item in a list. The recursive method is going to look like this:
So given a list like
[1,2,3]
, this is going to expand like so:This is how
member/2
is usually implemented:So you can see the recursive method is pretty simple and general.
Another simple but somewhat frowned-upon method is to simulate a failure to engage the backtracking mechanism. This is called a failure-driven loop and looks like this:
When you run this version of
print_all/1
, what happens is a little more complex than simple expansion.Verbally, the
fail
forces Prolog to back up to the last choice point it made and try using the next solution. Well,write/1
andnl/0
don't produce choice points because they only have one solution, butmember/2
does have multiple solutions—one for each item in the list. So Prolog takes each item out of the list and prints it. Finally whenmember/2
runs out of solutions, Prolog backs up to the previous choice point, which is the second body of theprint_all/1
predicate, which always succeeds. So the output looks the same. I think people nowadays generally prefer not to use failure-driven loops, but I don't understand the arguments well enough to parrot them usefully.One thing that may help you to see what's going on is use the
trace
predicate and step through the expansion of both versions, and see if you can make sense of the differences. My notation above is completely made up for this answer and may not be as clear.Looking back over what I originally wrote and your actual question:
foreach
is going to be deterministicmember
will always iterate in order, because lists are defined in such a way that you must access each item in turnMoreover, these days at least on S.O. you will get a lot of people telling you to use
maplist
and its ilk, so it's probably not just going to work, but also a good idea.