List 如何在Prolog中跟踪值?

List 如何在Prolog中跟踪值?,list,prolog,logical-purity,List,Prolog,Logical Purity,我是Prolog新手,在OOP中遇到了一些困难。我需要递归地运行一些字符,但记住我所经历的。在OOP中,我只需要创建一个数组或arraylist来跟踪我使用过的任何东西。然而,在Prolog中,我似乎找不到类似的方法来实现这一点。我该如何检查我已经使用了什么 确切的问题是,我想遍历一组字符,如果我两次遇到同一个字符,就停止。我的想法是将每一个都添加到一个列表中,然后检查下一个是否是列表中的成员 谢谢你的帮助 谢谢您可能会发现这很有用:从本质上讲,Prolog没有迭代,但它有递归。您需要在列表中循

我是Prolog新手,在OOP中遇到了一些困难。我需要递归地运行一些字符,但记住我所经历的。在OOP中,我只需要创建一个数组或arraylist来跟踪我使用过的任何东西。然而,在Prolog中,我似乎找不到类似的方法来实现这一点。我该如何检查我已经使用了什么

确切的问题是,我想遍历一组字符,如果我两次遇到同一个字符,就停止。我的想法是将每一个都添加到一个列表中,然后检查下一个是否是列表中的成员

谢谢你的帮助


谢谢

您可能会发现这很有用:从本质上讲,Prolog没有迭代,但它有递归。您需要在列表中循环执行您要执行的操作。

最基本的实现:

foo([], _).                  % 1
foo([X|Xs], Seen) :-         % 2
    \+ memberchk(X, Seen),   % 3
    foo(Xs, [X|Seen]).       % 4
  • 当列表为空(1)时,谓词成功
  • 如果列表不是空的(2):
    • 检查是否已看到列表的第一个元素(3)
    • 如果不是(
      \+X
      代表“X失败时成功”),则将元素添加到可见元素列表中,并检查列表的其余部分(4)
但我认为这不是你应该写的东西?由于不清楚您的最终目标是什么,因此很难提出更好的解决方案

一些提示:

  • 如果您只想查看列表中是否有重复项,您可以
  • 考虑对第二个参数使用一个而不是一个列表
  • 弄清楚你在干什么

    • 以下内容基于@Boris的回答;您还可以使用目标
      映射列表(dif(X),Seen)
      而不是
      \+memberchk(X,Seen)
      来保存:

      foo([],。%)1. foo([X | Xs],见):-%2 地图列表(dif(X),见),%3 foo(Xs[X|Seen])。%4.
      我认为问题在于如何找到列表中以第一个重复元素开头的后缀。如果我是对的,那么实现可以遵循以下路线:找到一个重复的元素,并使用一个返回所需后缀或失败的谓词,如果没有重复,则后缀为空。假设第一个参数是没有自由变量的正确闭合列表的实现可以是

      stop([],[]).
      stop([X|R],Rr) :-
        stop_at(R,X,Rr), !.
      stop([_|R],Rr) :-
        stop(R,Rr).
      
      stop_at([X|R],X,[X|R]) :- !.
      stop_at([_|R],X,Rr) :-
        stop_at(R,X,Rr).
      
      运行示例:

      ?- stop([a,b,c],R).
      R = [] ? ;
      no
      ?- stop([a,b,c,d,a,e,a,e],R).
      R = [a,e,a,e] ? ;
      no
      ?- stop([b,c,d,c,e],R).
      R = [c,e] ? ;
      no
      

      将找到的每个字符添加到列表中,然后使用此列表检查是否两次获得一个字符,这将与找到的字符列表的大小成线性关系。这可能是,也可能不是可接受的水渍险性能。你有多少不同的角色?仅ASCII可打印字符?完全Unicode?由于只有一些Prolog系统提供数组,因此要获得比列表更好的性能,下一个最佳选择是二叉搜索树,在一般情况下,它将为您提供O(log(n))。但你可以做得更好。当您标记问题时,您可以使用此系统读取黑树库(从YAP继承)。在最坏的情况下,这还应该为您提供O(log(n))。使用此库(作为在其他答案中使用列表的替代方案),您可以按照以下思路编写一些内容:

      :- use_module(library(rbtrees)).
      
      foo(List) :-
          rb_new(Seen),
          foo(List, Seen).
      
      foo([], _).
      foo([Head| Tail], Seen) :-
          (   rb_lookup(Head, _, Seen) ->
              true
          ;   rb_insert(Seen, Head, _, Seen2),
              foo(Tail, Seen2) 
          ).
      

      与使用列表相比,这是一个值得(性能方面的)替代方案吗?你没有提供足够的细节来回答这个问题。最好运行一些测试。还值得注意的是,在列表中插入是O(1)(当你做
      Head
      +
      list
      ->
      [Head | list]
      )但在红黑树中插入是O(log(n))。

      我意识到我必须使用递归,但我如何在执行过程中跟踪值?我不明白这个链接有什么帮助。有没有一种方法可以将一个列表初始化为空列表,并在每次递归调用后添加一个字符?@Jon,是的,这正是Prolog中常用的方法。以空列表开始递归,每次递归调用都可以向列表中添加一项。例如,如果您将列表
      L
      作为参数传入,您可以通过引用@Lower将
      X
      添加到头部,我将如何创建空数组?我试图创建一个谓词start([])。但这只是在我每次传递列表时将其重置为空数组。@Jon Prolog没有数组。它有列表。:)您需要携带一个额外的参数:
      start([],Result)
      或类似的内容。由于您在发布的问题描述中没有给出具体的示例,所以我不知道如何更具体。你应该用谷歌搜索“99个序言问题”。他们中的许多人处理列表,并给出答案。然后你会掌握基本列表处理的窍门。是的,我是说列表。我想我还在用其他语言思考。但谢谢你,至少我知道我的方向是正确的。如果你能给出一个问题的具体例子,以及你希望你的程序在这个问题上的表现,这将非常有帮助。否则,您将得到的只是一般性建议。@重复“是”,可能是更好的方法,我在回答中明确表示,我实际上不会编写这样的代码。但是对于这个特殊的问题,我认为引入
      maplist
      dif
      并不能改善答案。实际上,我建议你为后代写一个自己的答案。出于务实的原因,我会推迟选择实现,直到问题得到正确的定义:)但无论如何,这是一个很好的答案(+1)
      :- use_module(library(rbtrees)).
      
      foo(List) :-
          rb_new(Seen),
          foo(List, Seen).
      
      foo([], _).
      foo([Head| Tail], Seen) :-
          (   rb_lookup(Head, _, Seen) ->
              true
          ;   rb_insert(Seen, Head, _, Seen2),
              foo(Tail, Seen2) 
          ).