在列表列表中查找形状

时间:2018-08-14 17:07:36

标签: list prolog

程序说明

  1. 该计划的目的

我的程序用于计算形状在20X15尺寸平面中的位置。我有一个包含形状类型,其ID,其半径或高度以及其在平面上的预期[X,Y]位置的形状的列表。我有一个不同的二进制操作列表,其中仅包含形状类型,其id以及它与另一个形状的位置关系。有了操作列表中的此信息,我应该计算形状的[X,Y]位置:以下是两个列表的说明:

形状列表

我有一个形状列表:每个形状都是以下形式的列表:

[[shape, id],height/radius, [X,Y]]

由Prolog打印出来的这种形状的列表如下所示:

[[[diamond,1],4,[_7948,_7954]],[[circle,3],6,[_7894,_7900]],[[square,1],4,[_7840,_7846]],[[circle,1],5,[_7786,_7792]]|_7800]

操作列表

应该对每个操作采用以下形式的形状进行操作的列表:

[[[circle,1],below,[square,1]]]

这意味着圆1应该出现在X,Y平面上正方形1下方

通过序言打印出的此类列表如下所示:

[[[circle,1],below,[square,1]]|_8016]
  1. 程序

所以我有computeShapeLocations/2。它的第一个参数是操作列表,第二个参数是形状列表。它以递归方式遍历操作列表,以获取操作两侧的形状ID。例如circle 1 - below - sqaure 1,并将这两个形状发送到正确的函数以使用CLPFD计算位置。对于相对位置在“下方”的两个形状,我使用computeShapesBelow/2,它采用两种形状,每个形状的形式为[[shape, id],height/radius, [X,Y]]

ComputeShapeLocations / 2中的步骤:  1.从操作列表中获取形式为[[[circle,1],在下面,[square,1]]]的操作  2.先获取第一个ID(圆圈1),然后获取关系类型(如下),然后获取第二个ID(正方形1)。 3.从形状列表(ShapesOut)中获取形状 4.将形状发送到computeShapesBelow/2。这只是使用clpfd比较半径或高度以及X,Y平面的尺寸。

:- use_module(library(clpfd)).
computeShapeLocations([],_ShapesOut).
computeShapeLocations([Operation|Rest],ShapesOut) :- writeln(ShapesOut),                                                                             
                                      writeln([Operation|Rest]),                                                                             
                                      nth0(0,Operation,Subject1),                                                                            
                                      nth0(1,Operation,below),                                                                           
                                     nth0(2,Operation,Subject2),                                                                             
                                     Shape1 = [Subject1,H,Loc],                                                                          
                                     Shape2 = [Subject2,H2,Loc2],                                                                        
                                     member(Shape1,ShapesOut),
                                     member(Shape2,ShapesOut),                                                                  
                                     writeln(Shape1),                                                                            
                                     writeln(Shape2),                                                            
                                     writeln(Subject1),                                                                          
                                      writeln(Subject2),
                                     computeShapeBelow(Shape1,Shape2),                                       
                                  computeShapeLocations(Rest,ShapesOut).
computeShapeBelow(Shape1,Shape2) :- nth0(2,Shape1,Location1),                                    
                                    nth0(2,Shape2,Location2),                                                                            
                                    writeln(Shape1),                                                                             
                                    writeln(Shape2),                                                                             
                                   nth0(1,Shape1,Dim1),                                                                          
                                   nth0(1,Shape2,Dim2),                                                                          
                                   nth0(0,Location1,Xcord1),                                                                             
                                   nth0(0,Location2,Xcord2),                                                                          
                                  nth0(1,Location1,Ycord1),                                                                          
                                  nth0(1,Location2,Ycord2),                                                                       
                                  Ycord1 #> Dim1, Ycord1 #< 15-Dim1,                                                                          
                                  Xcord1 #> Dim1, Xcord1 #< 20-Dim1,                                                                          
                                  Ycord2 #> Dim2, Ycord2 #<  15-Dim2,                                                                         
                                  Xcord2 #> Dim2, Xcord2 #<  20-Dim2,                                                                         
                                  Ycord2 #> Ycord1+Dim2+Dim1.

问题:computeShapeLocations/2中,我的查询很奇怪(请参阅以上computeShapeLocations/2的第三步)。我使用member(ShapeId,ListOFshapesList)从给定了ID [shape,id]的listofshapes中提取形状。然后,我打印出结果(writeln(Shape1), writeln(Shape2),下图显示了行为如何错误。对于第一个形状(圆形,1),结果很好,computeShapesBelow/2甚至为其X,Y位置设置了适当的限制(6..14和6..9)。对于第二个形状(Shape2或正方形1)。它的行为不符合预期,并且clpfd限制导致较低的无穷大。

原因是因为第二次搜索[square,1]会忽略列表中的[[square, 1], 4, [_2166, _2172]]条目,而是以某种方式添加了额外的[[square, 1], _2250, [_2262|...]],然后用它来弄乱我的结果。 enter image description here

1 个答案:

答案 0 :(得分:3)

我认为,您的问题的根源被两个简单的问题所掩盖。我没有您所有的代码,并且我真的不知道您要做什么,所以我只说说我所看到的以及我将如何进行。

第一个问题是您没有有效利用统一。例如,您可以替换为:

nth0(0,Operation,Subject1),
nth0(1,Operation,below),
nth0(2,Operation,Subject2),

与此:

[Subject1,below,Subject2] = Operation,

但是,此外,您实际上并不需要Operation,因此可以将其移到子句的开头:

computeShapeLocations([[Subject1,below,Subject2]|Rest],ShapesOut) :-

当您开始进行这些更改时,您的代码将收缩很多,并且它会变得更容易看到正在发生的事情。使用更少的表述会更容易理解。例如,对于我来说,理解此命令列表中发生的事情要容易一些:

[below(circle(1), square(1)), below(circle(2), square(1)), ...]

甚至是此操作,您可以通过添加:- op声明来完成:

[circle(1) below square(1), circle(2) below square(1), ...]

,然后您的模式匹配将变得更加简单,例如:

compute(Shape1 below Shape2) :- ...

类似地,对于您的形状,如果您具有更多的结构,将会更容易理解正在发生的事情:

shape(circle(1), 4, X@Y)

对我来说比

更明显
[[circle,1], 4, [X,Y]]

我发现输入列表中有未绑定变量,这有点奇怪。我收集您是希望他们以后能获得价值。我想这种方法没有什么问题,我只是惊讶地看到混合使用地面和非地面作为输入。

您的第二个麻烦源是您将几种过程混合在一起。我很确定您在某处正在进行DCG解析步骤。通过解析其中的弱列表表示,您将迫使自己在这些方法中进行更多工作,从而破坏了列表并获得其含义。考虑:

command([Shape1,below,Shape2]) --> shape(Shape1), "below", shape(Shape2).

command(Shape1 below Shape2) --> shape(Shape1), "below", shape(2).

或者,

shape_type(circle) --> "circle".  shape_type(square) --> "square".
shape_name(shape(Name, Size, X@Y)) --> 
    shape_type(T), integer(ID), 
    integer(Size), 
    integer(X), integer(Y), 
    { Name =.. [T, ID] }.

相对于现在的情况。

IOW,您可以在解析期间创建结构,以简化处理过程。同样,在我看来,做很多像调试I / O一样的事情会使您的处理更加复杂。

find_shape(ShapeId, Shapes, Shape) :-
    Shape = shape(ShapeId, _, _),
    member(Shape, Shapes).

computeShapeLocations([], _).
computeShapeLocations([ShapeId1 below ShapeId2|Rest], Shapes) :-
    find_shape(ShapeId1, Shapes, Shape1),
    find_shape(ShapeId2, Shapes, Shape2),
    computeShapeBelow(Shape1, Shape2),
    computeShapeLocations(Rest, Shapes).

computeShapeBelow(shape(_, D1, X1@Y1), shape(_, D2, X2@Y2)) :-
    Y1 #> D1, Y1 #< 15 - D1,
    X1 #> D1, X1 #< 20 - D1,
    Y2 #> D2, Y2 #< 15 - D2,
    X2 #> D2, X2 #< 20 - D2,
    Y2 #> Y1 + D2 + D1.

我想,如果我盯着这个看,会发现调试起来会容易一些。