执行swi-prolog和其他列表元素的操作

cod*_*hot 12 prolog

如何按顺序对列表中的每个元素执行操作?

基于这两个资源:

  1. http://www.swi-prolog.org/pldoc/doc/swi/library/lists.pl
  2. http://www.swi-prolog.org/pldoc/doc_for?object=foreach/2

我想我总是可以依靠:

  • 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)

口头上,failProlog 的力量支持它所做的最后一个选择点并尝试使用下一个解决方案.好吧,write/1并且nl/0不生成选择点,因为它们只有一个解决方案,但member/2 确实有多个解决方案 - 列表中的每个项目都有一个.因此Prolog将每个项目从列表中删除并打印出来.最后,当member/2解决方案耗尽时,Prolog会回到上一个选择点,这是print_all/1谓词的第二个主体,它始终成功.所以输出看起来一样.我认为现在人们通常不喜欢使用故障驱动的循环,但是我不能很好地理解这些论点以便有用地鹦鹉学舌.

有一件事可以帮助您了解正在发生的事情是使用trace谓词并逐步扩展两个版本,看看您是否能够理解这些差异.我的上述注释完全弥补了这个答案,可能不太清楚.

回顾我最初写的内容和您的实际问题:

  • foreach 将是确定性的
  • member 将始终按顺序迭代,因为列表的定义方式必须依次访问每个项目

而且,现在至少在SO上你会得到很多人告诉你使用maplist它们之类的东西,所以它可能不仅仅是起作用,而且也是一个好主意.