对swi-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谓词,因为我喜欢在我自己的谓词,仍然总是按顺序迭代?

是的,但是您必须担心谓词失败。如果可以,则不会处理列表中的其余元素,因为它产生连接而不是故障驱动的循环。

我将更热衷于使用maplist/2,因为我认为它比foreach/2使用更广泛,但我以前也没有见过这个选项。:)

Edit:让我们讨论一下我所说的失败驱动循环。

在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/1nl/0不产生选择点,因为它们只有一个解决方案,但member/2 确实有多个解决方案——列表中的每个项目一个。所以Prolog从列表中取出每一项并打印出来。最后,当member/2解决方案用完时,Prolog返回到之前的选择点,这是print_all/1谓词的第二个主体,它总是成功的。所以输出看起来是一样的。我认为现在的人们通常不喜欢使用失败驱动的循环,但我不太理解这些论点,无法有效地模仿它们。

有一件事可以帮助您了解发生了什么,那就是使用trace谓词并逐步进行两个版本的扩展,看看您是否可以理解其中的差异。我上面的符号完全是为了这个答案而编的,可能不那么清楚。

回顾我最初写的东西和你的实际问题:

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

此外,这些天,至少在S.O.上,你会得到很多人告诉你使用maplist和类似的,所以它可能不仅仅是工作,而且是一个好主意。

最新更新