简单的prolog查询

时间:2015-08-03 09:11:11

标签: prolog

我对prolog很新,虽然我读了一些书,但我可以肯定地说我的编程大脑无法想到Prolog的方式。我想解决的问题非常简单(我相信)。我将通过一个例子来描述它。

假设我有一个图表,其中包含4个“类型”的节点和3个连接节点的边。类型可以是A,B,C或D,如下图所示(见图1),A可以与B和C(分别为A_To_B和A_To_C边)连接,而C可以​​连接到D( C_To_D边缘)。还有一个未在图片上显示的附加规则:A最多可连接1 C.

Figure 1

我想在Prolog中表达这些简单的规则来解决第二张图中显示的问题。有3个节点缺少类型(标记为X?,Y?和Z?)。通过在我的脑海中应用上述规则,我可以很容易地找到X?和Z?是B型(A可以连接不超过1 C)和Y?是D类型,因为C只能连接到D.

enter image description here

请为我提供任何帮助吗?我不是只是为了选择解决方案。我也想学习Prolog,所以对于一本能够解释Prolog的书的任何建议,对于那些从未像我这样从未有过这样的概念的人会非常受欢迎。

编辑:失败的示例

我想出了以下两个例子:

enter image description here

例如1,规则是

can_connect(a,b,_).
can_connect(a,c,1).

link(1,2).

type(1,a).
type(2,_).

返回的可能解决方案是[b,c],这是正确的,因为我们要求最多 1从A到C的链接,这意味着0链接也是可以接受的。

在示例2中,规则更改为以下内容:

can_connect(a,b,_).
can_connect(a,c,**2**).

link(1,2).
link(1,3).

type(1,a).
type(2,_).
type(3,c).

在这里运行代码会返回[c],这是错误的。 b也是一个可接受的解决方案,因为我们需要再次最多 2个A到C链接,这意味着只有1个就可以了。

我本周末试图找出解决方案。首先,我认为它在示例1中的工作原理只是因为在建议的解决方案中没有实例化A到C的链接(其中检查2是否可以是b),因此can_connect(a,c,1)不是检查,以便建议的解决方案被接受。在示例2中,已经存在一个A到C链接,因此检查了can_connect(a,c,2),并且拒绝了节点2具有类型b的解决方案,因为规则检查是否存在完全 2而不是最多 2个从A到C的链接。

我找到了一种适用于这些情况的解决方案,但在其他情况下失败了。这是:

% value #3 is the lower bound and #4 is the upper bound.
can_connect(a,b,0,500).
% A C node can be connected by 0, 1 or 2 A nodes
can_connect(a,c,0,2).
can_connect(d,c,1,1).
can_connect(c,e,0,1).

%The same as previous solution
link(1,2).
link(1,3).

% No change here
type(1,a).
type(2,_).
type(3,c).

% No change here
node_type(N, NT) :- 
    type(N, NT), 
    nonvar(NT), 
    !. % assume a node has only one type

% No change here
node_type(N, NT) :-
    assoc_types(Typed),
    maplist(check_connections(Typed), Typed),
    memberchk(N:NT, Typed).

% No change here
assoc_types(Typed) :-
    findall(N, type(N, _), L),
    maplist(typed, L, Typed).

% No change here
typed(N, N:T) :- 
    type(N, T),
    member(T, [a,b,c]).

% Changes here
check_connections(Graph, N:NT) :-
    forall(link(N, M), (
        memberchk(M:MT, Graph),
        can_connect(NT, MT, L, U),
        findall(X, (link(N, X), memberchk(X:MT, Graph)), Ts),
        mybetween(L, U, Ts),
        forall(can_connect(NT, Y, LM, UM), (
            findall(P, (link(N,P),memberchk(P:Y, Graph)), Ss),
            length(Ss, SsSize ),
            SsSize>=LM,
            SsSize=<UM
        ))
    )).

% It is used to find if the length of a list is between two limits.
mybetween(Lower, Upper, MyList) :-
    length(MyList, MySize),
    MySize=<Upper,
    MySize>=Lower.

此示例中的此解决方案失败

enter image description here

在这个例子中,X?必须总是b,Y?必须始终是C和Z?必须永远是D.它找到X?和Y?正确但不是Z?我相信经过一些调试后,这是因为在当前实现中我只检查与节点开始的链接相关的can​​_connect规则而不是结束到一个节点。但是,我完全不确定。

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

问题的表示需要消除节点名称的歧义,因此我们可以恰当地表达链接

enter image description here

现在我们可以写

can_connect(a,b,_).
can_connect(a,c,1).
can_connect(c,d,_).

link(1,2).
link(1,3).
link(1,4).
link(4,5).
link(4,6).
link(7,4).
link(7,8).

type(1,a).
type(2,b).
type(3,_).
type(4,c).
type(5,d).
type(6,_).
type(7,a).
type(8,_).

Prolog中的下划线(匿名变量)在SQL中扮演类似于NULL的角色,它可以假定任何值。

所以,第一个片段

node_type(N, NT) :- type(N, NT), nonvar(NT), !. % assume a node has only one type

可用于表达我们对此问题的了解。

事实can_connect / 3然后可以像

一样阅读
a can connect to any number of b
a can connect to just 1 c
etc

如果我们不知道节点类型,则需要一个复杂的规则,它从目标节点的类型推断源节点的类型,并考虑计数约束,如

node_type(N, NT) :-
    link(M, N),
    type(M, MT),
    can_connect(MT, NT, C),
    aggregate(count, Y^(link(M, Y), type(Y, NT)), C).

?- forall(between(1,8,N), (node_type(N,T),writeln(N:T))).
1:a
2:b
3:b
4:c
5:d
6:d
7:a
8:b
true.

编辑如果你的Prolog没有库(aggregate),从哪里加载了aggregate / 3,你可以试试

node_type(N, NT) :-
    link(M, N),
    type(M, MT),
    can_connect(MT, NT, C),
    findall(t, (link(M, Y), type(Y, NT)), Ts), length(Ts, C).

编辑首先是更新后的图表,标有已知类型:

updated graph

我以前的代码只能在非常有限的假设下工作。这是一个更通用的东西,它使用'生成和测试'方法检查完整图形上的约束(如@false注释所示)。

node_type(N, NT) :-
    assoc_types(Typed),
    maplist(check_connections(Typed), Typed),
    memberchk(N:NT, Typed).

assoc_types(Typed) :-
    findall(N, type(N, _), L),
    maplist(typed, L, Typed).

typed(N, N:T) :- type(N, T), member(T, [a,b,c,d]).

check_connections(Graph, N:NT) :-
    forall(link(N, M), (
        memberchk(M:MT, Graph),
        can_connect(NT, MT, C),
        aggregate(count, X^(link(N, X), memberchk(X:MT, Graph)), C)
    )).

现在?- node_type(4,X).失败了......

相关问题