Data structures 在Prolog中创建队列结构

Data structures 在Prolog中创建队列结构,data-structures,prolog,queue,structure,custom-data-type,Data Structures,Prolog,Queue,Structure,Custom Data Type,我正在学习Prolog,在学习面向对象的语言时遇到了困难 我试图完成以下说明: 实现分阶段队列。这是一个由两个列表组成的结构: 前端和后端,表示为队列(前端、后端)。这个 如果两个列表都为空,则队列为空。如果将元素添加到 将它们添加到后端的队列;如果它们被移除,它们将被移除 从前端移除;如果前端变空(且 后端不为空)则后端将成为具有[] 作为新的后端。例如,我们可以从如下队列开始 队列([2,5,7],]),添加8将为我们提供队列([2,5,7],[8]),删除两个 元素给出队列([7],[8]

我正在学习Prolog,在学习面向对象的语言时遇到了困难

我试图完成以下说明:

实现分阶段队列。这是一个由两个列表组成的结构: 前端和后端,表示为
队列(前端、后端)
。这个 如果两个列表都为空,则队列为空。如果将元素添加到 将它们添加到后端的队列;如果它们被移除,它们将被移除 从前端移除;如果前端变空(且 后端不为空)则后端将成为具有
[]
作为新的后端。例如,我们可以从如下队列开始
队列([2,5,7],])
,添加8将为我们提供
队列([2,5,7],[8])
,删除两个 元素给出
队列([7],[8])
,添加9给出
队列([7],[9,8])
,以及 删除一个元素会产生
队列([9,8],])

我不明白我是如何创建并引用
.pl
文件中的队列结构的,而其他谓词可以通过这种方式进行操作和转换

我大致勾勒出了我认为我应该做的事情,既定义了队列结构,也只是一个列表列表

将|添加到| q(X,[[H | T]|[H2 | T2],[[H | T]|[X |[H2 | T2]])。
队列(X,Y)
将_添加到_q(A,队列(X,Y),队列(X,[A | Y])。%给出语法错误:应为运算符
------------------
从_q([[H |[T | T3]][H2 | T2]],[[T | T3]][H2 | T2]]中移除_)。
队列(X,Y)
从_q(队列(X,[H | T])、队列(H,T)中移除_。
如何在Prolog中定义和使用结构,如何添加OO语言中的内容方法,例如
getHead
getTail
,我已经看到了一些示例,说明了如何仅使用列表来实现这一点,但我不是使用列表列表,而是使用两个单独列表的“队列”

感觉失落

我很难理解来自OO背景的语言


帮你自己一个忙,在学习Prolog的时候忘记你对OO的了解,这只会让你在学习Prolog的时候更加困惑。换句话说,不要想OO的概念,那么我该如何把它翻译成Prolog。思考一下如何构建越来越复杂的谓词

?-
    empty(Q),
    add(7,Q,Q2),
    add(5,Q2,Q3),
    add(2,Q3,Q4),
    write(Q4),nl,
    remove(Q4,A,Q5),
    add(3,Q5,Q6),
    remove(Q6,B,Q7),
    remove(Q7,C,Q8),
    remove(Q8,D,Q9),
    empty(Q9),
    write([A,B,C,D]).

我不明白我是如何在PL文件中创建并引用队列结构的,而其他谓词可以通过这种方式进行操作和转换

这些说明为您提供了数据结构的基础,即

queue(Front,Back)
前面和后面是一个列表。列表的示例

[]
[a]
[a,b]
[a|b]
引用队列很容易。由于Prolog使用语法统一,统一的一面是您想要统一的原子,例如,
queue(Front,Back)
,而统一的另一面是
queue(Front,Back)
的转换,您可以在编写的谓词中使用它们

你已经用电脑演示过了

add_to_q(A,queue(X,Y),queue(X,[A|Y])
请注意,它缺少一个结尾


它缺少一个结尾


由于指令给出的示例非常有限,因此很难创建许多测试用例来确保代码在实际生产系统中工作

下面是基于这个问题的工作代码

add(Item,queue(Front,Back),queue(Front,[Item|Back])).

remove(Item,queue([Item|[]],Back),queue(Back,[])).
remove(Item,queue([Item|Front],Back),queue(Front,Back)).

:- begin_tests(funny_queue).

funny_queue_test_case_generator(add   , 8    ,queue([2,5,7]  ,[   ] ) ,queue([2,5,7],[8]   ) ).
funny_queue_test_case_generator(remove, 2    ,queue([2,5,7]  ,[  8] ) ,queue([5,7]  ,[8]   ) ).
funny_queue_test_case_generator(remove, 5    ,queue([5,7]    ,[  8] ) ,queue([7]    ,[8]   ) ).
funny_queue_test_case_generator(add   , 9    ,queue([7]      ,[  8] ) ,queue([7]    ,[9,8] ) ).
funny_queue_test_case_generator(remove, 7    ,queue([7]      ,[9,8] ) ,queue([9,8]  ,[]    ) ).

test(add,[forall(funny_queue_test_case_generator(add,Item,Funny_queue_0,Funny_queue))]) :-
    add(Item,Funny_queue_0,Funny_queue_expected),

    assertion( Funny_queue == Funny_queue_expected ).

test(add,[nondet,forall(funny_queue_test_case_generator(remove,Item,Funny_queue_0,Funny_queue))]) :-
    remove(Item,Funny_queue_0,Funny_queue_expected),

    assertion( Funny_queue == Funny_queue_expected ).

:- end_tests(funny_queue).

这是一个有趣的队列。当队列的后面移到前面时,项目以相反的顺序弹出。这不是真正的排队

不过,这些行动相当直接

首先,我们将把队列表示为复合术语
queue(X,Y)
,其中
X
Y
是表示队列前面和后面的列表

我们需要一种获取空队列的方法:

empty(queue([],[])).
要将项目添加到队列,请执行以下操作:

add(A,queue(X,Y),queue(X,[A|Y])).
remove(queue([A|T],X),A,queue(T,X)).
现在,说明不一致。他们说两个空列表代表一个空队列,新项目总是添加到后面的列表中。那么,如果第一个列表开始为空并且从未添加到中,它怎么可能变为空呢

因此,我们必须提供一个谓词,它允许我们从一个空的前列表中删除,只要后列表不是空的

remove(queue([],[A|X]),A,queue(X,[])).
这会将后面列表的尾部带到前面,并将尾部的第一个元素返回到
A

最后,如果前面的列表不是空的,那么我们执行以下操作:

add(A,queue(X,Y),queue(X,[A|Y])).
remove(queue([A|T],X),A,queue(T,X)).
让我们测试这4个谓词

?-
    empty(Q),
    add(7,Q,Q2),
    add(5,Q2,Q3),
    add(2,Q3,Q4),
    write(Q4),nl,
    remove(Q4,A,Q5),
    add(3,Q5,Q6),
    remove(Q6,B,Q7),
    remove(Q7,C,Q8),
    remove(Q8,D,Q9),
    empty(Q9),
    write([A,B,C,D]).
Q9
在向队列添加4个原子并将其删除后必须为空<代码>[A、B、C、D]统一为
[2,5,7,3]

Q4
是说明中描述的开始状态,尽管它是
队列([],[2,5,7])
,因为我们被指示总是添加到后面的列表中

所以这一切都是按照我们被指示的方式进行的

但这不是真正的排队

如果这是一个真正的队列,那么添加的第一项应该是删除的第一项。预期输出为
[7,5,2,3]
。为了实现这一点,我们将重写第一个
remove
谓词,如下所示:

remove(queue([],Z),A,queue(X,[])) :- reverse(Z,[A|X]).
reverse(X,Y):- reverse(X,[],Y).
reverse([],A,A).
reverse([H|T],A,R) :- reverse(T,[H|A],R). 
运行带有该更改的代码可以得到队列的预期输出。

所说的。什么都忘了。这都是关于模式匹配的

这是您所需要的全部:

  • 首先,创建空队列的方法:
  • 一旦你做到了这一点,排队买东西就变得微不足道了。 这只是将已排队的项目预先添加到“后退”队列的问题:

    说明: prolog中的列表是递归数据结构。按照惯例,空列表是无效的 表示为原子
    []
    (将其视为字符串)。写入非空列表 使用方括号表示法(
    [1]
    [1,2]
    等)。但是那只是句法上的 数据结构的糖
    /2

    • [1]
      (1,[])
    • [1,2]
      (1,2,[])
    • ……等等
    <
    enque( X , q(F,B) , q(F, [X|B] ) ) .
    
    deque( q( []     , Bs ) , F , q( Fs , [] ) ) :- reverse( Bs, [F|Fs] ).
    deque( q( [F|Fs] , Bs ) , F , q( Fs , Bs ) ) .
    
    
    empty( q([],[]) ).
    
    enque( X , q(F,B) , q(F, [X|B] ) ).
    
    deque( q( []     , Bs ) , F , q(Fs,[]) ) :- reverse( Bs, [F|Fs] ).
    deque( q( [F|Fs] , Bs ) , F , q(Fs,Bs) ).
    
    
    run_example :-
      empty( Q0             ) , writeln( queue :         Q0  ) ,
      enque( 1   , Q0 , Q1  ) , writeln( enque :         Q1  ) ,
      enque( 2   , Q1 , Q2  ) , writeln( enque :         Q2  ) ,
      enque( 3   , Q2 , Q3  ) , writeln( enque :         Q3  ) ,
      enque( 4   , Q3 , Q4  ) , writeln( enque :         Q4  ) ,
      deque( Q4  , X1 , Q5  ) , writeln( deque : x(X1) : Q5  ) ,
      enque( 5   , Q5 , Q6  ) , writeln( enque :         Q6  ) ,
      enque( 6   , Q6 , Q7  ) , writeln( enque :         Q7  ) ,
      deque( Q7  , X2 , Q8  ) , writeln( deque : x(X2) : Q8  ) ,
      deque( Q7  , X3 , Q9  ) , writeln( deque : x(X3) : Q9  ) ,
      deque( Q9  , X4 , Q10 ) , writeln( deque : x(X4) : Q10 ) ,
      deque( Q10 , X5 , Q11 ) , writeln( deque : x(X5) : Q11 ) ,
      deque( Q11 , X6 , Q12 ) , writeln( deque : x(X6) : Q12 ) ,
      deque( Q12 , X7 , Q13 ) , writeln( deque : x(X7) : Q13 ) ,
      empty( Q13            )
      .