如何在Prolog中生成谓词网格?

如何在Prolog中生成谓词网格?,prolog,swi-prolog,Prolog,Swi Prolog,我正在尝试用Prolog创建一个程序,但我对它还是新手,不确定在Prolog中生成网格状结构的最佳方法是什么 例如,假设我有一个谓词cell/4,它有4个参数北部,西部,南部和东部。我要创建的是谓词cell/4的NxN网格,它根据它们在网格中的位置相互连接 例如,我可以手动创建2x2网格。预期结果如下: North_1 North_2 |

我正在尝试用Prolog创建一个程序,但我对它还是新手,不确定在Prolog中生成网格状结构的最佳方法是什么

例如,假设我有一个谓词
cell/4
,它有4个参数<代码>北部,
西部
南部
东部
。我要创建的是谓词
cell/4
的NxN网格,它根据它们在网格中的位置相互连接

例如,我可以手动创建2x2网格。预期结果如下:

               North_1                               North_2
                  |                                     |
        +---------+----------+                +---------+----------+
        |       North        |                |       North        |
        |                    | Interconnect_1 |                    |
West_1 -+ West  cell/4  East +----------------+ West  cell/4  East +- East_1
        |                    |                |                    |
        |       South        |                |       South        |
        +---------+----------+                +---------+----------+
                  |                                     |
            Interconnect_2                        Interconnect_3
                  |                                     |
        +---------+----------+                +---------+----------+
        |       North        |                |       North        |
        |                    | Interconnect_4 |                    |
West_2 -+ West  cell/4  East +----------------+ West  cell/4  East +- East_2
        |                    |                |                    |
        |       South        |                |       South        |
        +---------+----------+                +---------+----------+
                  |                                     |
               South_1                               South_2
所以我的问题是,如何将这个过程推广到NxN网格?我相信可以有很好的方法来处理
问题的参数
谓词,使其成为更一般的
问题(北、西、南、东)
,其中每个参数都是一个列表。我正在努力生成这种类似网格的结构。

假设
单元(X,Y)
表示坐标
X
Y
处的网格单元,其中
X
Y
大于
0
。现在,你可以写一些关于网格单元的有趣的东西了。例如,您可以捕获单元格有效的含义

valid(cell(X, Y)) :- X > 0, Y > 0.
或者捕获关系,如
右邻居
左邻居
,等等

right_neighbor(cell(X, Y1), cell(X, Y2)) :-
    Y2 is Y1 + 1,
    valid(cell(X, Y1)),
    valid(cell(X, Y2)).

left_neighbor(cell(X, Y1), cell(X, Y2)) :-
    Y1 is Y2 + 1,
    valid(cell(X, Y1)),
    valid(cell(X, Y2)).
您可以定义具有
N
列的网格的
R
第行,如下所示

grid_row(R, 1, [cell(R, 1)]) :-
    valid(cell(R, 1)).

grid_row(R, N, [cell(R, N) | Row]) :-
    M is N - 1,
    valid(cell(R, M)),
    grid_row(R, M, Row).
problem_row([N], W, [S], E, [cell(N, W, S, E)]).

problem_row([N|Ns], W, [S|Ss], E, [cell(N, W, S, I)|Cs]) :-
    problem_row(Ns, I, Ss, E, Cs).
problem(Ns, [W], Ss, [E], Cs) :-
    problem_row(Ns, W, Ss, E, Cs).

problem(Ns, [W|Ws], Ss, [E|Es], Cs) :-
    problem_row(Ns, W, Is, E, R),
    problem(Is, Ws, Ss, Es, Rs),
    append(R, Rs, Cs).
然后,通过
N
列定义一个由
M
行组成的网格就很简单了

grid(1, N, [Row]) :-
    grid_row(1, N, Row).

grid(M, N, [Row | Rows]) :-
    grid_row(M, N, Row),
    P is M - 1,
    grid(P, N, Rows).
例如,查询
网格(3,2,X)。
生成:

X = [[cell(3, 2), cell(3, 1)], [cell(2, 2), cell(2, 1)], [cell(1, 2), cell(1, 1)]]
R = [cell(n1, w1, s1, _1428), cell(n2, _1428, s2, e1)]
G = [cell(n1, w1, _1460, _1462), cell(n2, _1462, _1476, e1),
     cell(_1460, w2, s1, _1494), cell(_1476, _1494, s2, e2)]
如果我们使用表示法
单元(北、西、南、东)
其中
西
分别是其北、西、南和东单元的单元连接点(如果有),则解决方案类似。我们首先定义一行的单元格,如下所示

grid_row(R, 1, [cell(R, 1)]) :-
    valid(cell(R, 1)).

grid_row(R, N, [cell(R, N) | Row]) :-
    M is N - 1,
    valid(cell(R, M)),
    grid_row(R, M, Row).
problem_row([N], W, [S], E, [cell(N, W, S, E)]).

problem_row([N|Ns], W, [S|Ss], E, [cell(N, W, S, I)|Cs]) :-
    problem_row(Ns, I, Ss, E, Cs).
problem(Ns, [W], Ss, [E], Cs) :-
    problem_row(Ns, W, Ss, E, Cs).

problem(Ns, [W|Ws], Ss, [E|Es], Cs) :-
    problem_row(Ns, W, Is, E, R),
    problem(Is, Ws, Ss, Es, Rs),
    append(R, Rs, Cs).
查询问题行([n1,n2],w1[s1,s2],e1,R)。产生:

X = [[cell(3, 2), cell(3, 1)], [cell(2, 2), cell(2, 1)], [cell(1, 2), cell(1, 1)]]
R = [cell(n1, w1, s1, _1428), cell(n2, _1428, s2, e1)]
G = [cell(n1, w1, _1460, _1462), cell(n2, _1462, _1476, e1),
     cell(_1460, w2, s1, _1494), cell(_1476, _1494, s2, e2)]
我们将
问题
谓词定义如下

grid_row(R, 1, [cell(R, 1)]) :-
    valid(cell(R, 1)).

grid_row(R, N, [cell(R, N) | Row]) :-
    M is N - 1,
    valid(cell(R, M)),
    grid_row(R, M, Row).
problem_row([N], W, [S], E, [cell(N, W, S, E)]).

problem_row([N|Ns], W, [S|Ss], E, [cell(N, W, S, I)|Cs]) :-
    problem_row(Ns, I, Ss, E, Cs).
problem(Ns, [W], Ss, [E], Cs) :-
    problem_row(Ns, W, Ss, E, Cs).

problem(Ns, [W|Ws], Ss, [E|Es], Cs) :-
    problem_row(Ns, W, Is, E, R),
    problem(Is, Ws, Ss, Es, Rs),
    append(R, Rs, Cs).
查询
问题([n1,n2],[w1,w2],[s1,s2],[e1,e2],G)
产生:

X = [[cell(3, 2), cell(3, 1)], [cell(2, 2), cell(2, 1)], [cell(1, 2), cell(1, 1)]]
R = [cell(n1, w1, s1, _1428), cell(n2, _1428, s2, e1)]
G = [cell(n1, w1, _1460, _1462), cell(n2, _1462, _1476, e1),
     cell(_1460, w2, s1, _1494), cell(_1476, _1494, s2, e2)]

您应该注意,我已经将您的
单元格
谓词具体化为一个函子,以避免在运行时必须向程序中添加子句,这很可能不是您想要的。另外,请注意,网格答案包含问题中要求的未绑定变量。你可以为它们创造原子。例如,通过连接北原子和西原子的每一个组合。

我很难想象你会喜欢
单元格/4
,因为你有四个指向其他方向的指针,但这些单元格中的内容只是指向其他单元格的指针,而这些单元格只是指向其他单元格的指针。。。我想你可能真的想要
单元格/5
,它更像是某个值加上每个方向的指针

通常,如果您想要大小为N的列表,可以使用
length/2
为您生成一个如下所示的列表:

?- length(L, 3).
L = [_772, _778, _784].
然后你可以传递变量列表。假设你正在制作一个迷宫或其他东西,你想把你的网格传递给一个进程,这个进程会在其中放置一些东西,比如说,壁虎,墙等等,这就是你想要这个网格的原因。我上面的评论是想说,在Mx1安排中,这种结构类似于一个双链接列表:

            +--------------------+                +--------------------+
            |                    | Interconnect_1 |                    | 
    West_1 -+ West  cell/2  East +----------------+ West  cell/2  East +- East_1
            |                    |                |                    |
            +--------------------+                +--------------------+  
您可以通过类似的方式手动构建此结构:

?- West = cell(West_1, East), East = cell(West, East_1).
West = cell(West_1, cell(West, East_1)),
East = cell(West, East_1).
@false正确地指出这将是递归的,因为West等于某个结构,其中包含West。我和他一样对此感到担忧,因为无限项带来了有趣的问题,而且,通常在遍历过程中,您只需保持前面的值,就可以避免问题。(在你的例子中,这将形成一个网格,我猜只有东指针和南指针,或者一个纬度和一个纵向的组合,而不是两者都有)

在任何情况下,您都可以按照
length/2
的示例,输入所需的长度,然后一次构建一个节点,从而构建一个双链接列表:

generate(0, Prev, cell(Prev, _)).
generate(N, Prev, cell(Prev, Next)) :-
    succ(N0, N),
    generate(N0, cell(Prev, Next), Next).
这是N=3的情况:

?- generate(3, Start, X).
X = cell(Start, cell(_S1, cell(_S2, _S3))), % where
    _S1 = cell(Start, cell(_S1, cell(_S2, _S3))),
    _S2 = cell(_S1, cell(_S2, _S3)),
    _S3 = cell(cell(_S2, _S3), _656) 
再次,让我指出,单链表中的cons单元格类似于
cell/2
,因为有一个值和一个下一个指针,所以我们可能需要向其添加一个值槽,并返回
cell/3
结构

因此,回到网格,生成NxN网格可能需要做的事情类似于一次生成一行,保留每次生成的前一行,并将其传递给某种压缩过程,该过程将前一行的
指针等同于当前行的
指针

我这里有一个单链接网格的解决方案。我希望这足以满足您的需要。这是一个有点棘手的想出

首先,我们需要能够生成一行:

generate_row(1, cell(_, nil, _)).
generate_row(N, cell(_, Next, _)) :-
    succ(N0, N),
    generate_row(N0, Next).
这里的计划是我们有一个
单元(Value,NextRight,NextDown)
结构。
NextRight
NextDown
都是单元格,分别是网格中的东方向和南方向。我使用
nil
表示空列表的作用;它终止递归并表示空指针。这是一个重要的东西,因为否则我的缝合过程将有无限递归

现在我们有了一行,让我们来考虑如何组合上一行和下一行。我们在这里真正做的就是从左到右遍历两行,将上一行的下一行与第二个列表中它下面的单元格相等。读起来有点奇怪,但它确实有用:

stitch(cell(_, NextAbove, Below), Below) :-
    Below = cell(_, NextBelow, _),
    stitch(NextAbove, NextBelow).
stitch(cell(_, nil, Below), Below).
因为我们需要
下面的
来保持完整,所以我们把它从身体而不是头部分开。我在这里匹配
nil
以终止递归

现在我们有了所有我们需要的东西