2011-09-24 62 views

回答

16

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

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

编辑:让我们来讨论我的意思是关于故障驱动循环。

在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力量序言备份到最后选择点它制成,尝试使用一个解决方案。那么,write/1nl/0不会产生选择点,因为他们只有一个解决方案,但member/2确实有多个解决方案 - 一个用于列表中的每个项目。所以Prolog从列表中取出每个项目并打印出来。最后,当member/2用完解决方案时,Prolog备份到之前的选择点,这是print_all/1谓词的第二个主体,它始终成功。所以输出看起来一样。我认为现在人们普遍宁愿不使用失败驱动的循环,但我不能很好地理解这些论点,以便有用地鹦鹉。

有一件事可以帮助你看到发生了什么是使用trace谓词,并逐步扩展两个版本,并看看你能否理解这些差异。我上面的记号完全是为了这个答案而编写的,可能不太清楚。

回首我原来写您的实际问题:

  • foreach将是确定性的
  • member总是为了迭代,因为列表中,这样你必须访问的方式定义每个项目轮流

而且,这些天至少在SO你会得到很多人告诉你使用maplist之流,所以它可能不只是工作,但也是一个好主意。

+0

我看到如此建立一个列表,其中我想要所有的解决方案,然后maplist通过循环打印them.I认为将需要一个故障驱动循环在那里某处因为我的程序是非常基于证据的而不是基于计算的。 – codeshot

+0

无论如何应该始终可以做到这一点。如果可以的话,我建议避免失败驱动的循环,但如果它更合理,请使用它。 –

+0

我对maplist/2的文档的阅读是,列表可以重新排序,这意味着动作将以任意顺序执行。这意味着它不能解决问题。 递归增加了复杂性,没有描述我的意图,但我认为foldl/4以合理的描述性方式实现我需要的东西,如果我只是提供一个适用的垫片,接受两个额外的参数来累积任何东西:act(A,_, _):call(A) – codeshot

相关问题