对swi prolog和其他文件中的每个列表元素执行操作

对swi prolog和其他文件中的每个列表元素执行操作,prolog,Prolog,如何按顺序对列表中的每个元素执行操作 基于这两种资源: 我想我可以一直依靠: foreach(成员(X,[1,2]),write(X))。 这是确定性的吗?我可以在自己的谓词中随意包装member/2谓词并始终按顺序迭代吗?是的,但您必须担心谓词失败。如果可以,列表中的其余元素将不会被处理,因为它会生成一个连接,而不是一个故障驱动的循环 我会更热衷于使用maplist/2,因为我认为它比foreach/2使用得更广泛,但我以前也没有见过这个选项。:) 编辑:让我们讨论一下我所说的故障驱动

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

基于这两种资源:

  • 我想我可以一直依靠:

    • foreach(成员(X,[1,2]),write(X))。

    这是确定性的吗?我可以在自己的谓词中随意包装member/2谓词并始终按顺序迭代吗?

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

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

    编辑:让我们讨论一下我所说的故障驱动循环的含义

    Prolog中有两种基本的迭代方法:递归和故障驱动循环。假设我想打印列表中的每一项。递归方法如下所示:

    print_all([]).
    print_all([X|Rest]) :- write(X), nl, print_all(Rest).
    
    print_all(List) :- member(X, List), write(X), nl, fail.
    print_all(_).
    
    因此,给定一个类似于
    [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([]).
    print_all([X|Rest]) :- write(X), nl, print_all(Rest).
    
    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
    及其类似的工具,所以这可能不仅会起作用,而且是一个好主意。

    我明白了,在我想要所有解决方案的地方,用谓词的失败驱动循环构建一个列表,然后在循环上映射列表来打印它们。我想在那里的某个地方需要一个失败驱动的循环,因为我的程序是基于证明的,而不是基于计算的。应该总是可以这样做。如果可以的话,我建议避免失败驱动的循环,但是如果它更有意义的话,就使用它。我对maplist/2文档的理解是,列表可以重新排序,这意味着操作将以任意顺序执行。这意味着它不能解决问题。递归增加了不描述我的意图的复杂性,但我认为foldl/4以合理的描述方式实现了我所需要的,如果我只是在应用程序周围提供一个垫片,它接受两个额外的参数而不累积任何内容:act(a,,,):call(a)@codeshot我认为操作不能以任意顺序执行。毕竟,列表必须构建并前后遍历,这就是链表的工作方式。您在文档中的何处看到了这一点?还有,五年后…?疯了?这是一种非常粗鲁的方式,让人仔细检查他们的工作。