繁体   English   中英

在 Prolog 中实现自动机

[英]Implementing an Automaton in Prolog

我是 Prolog 的新手。 我设法相对较快地学习了 C 和 Java,但是 Prolog 给我带来了很多麻烦。 我的麻烦是理解列表和编写函数? 例如。 我们有这个自动机:

在此处输入图像描述

我可以用 C 和 Java 完成这项任务,没有问题。 但是课程需要Prolog。 以我目前的知识,我可以做这样的事情:

% 1. Check whether all integers of the list are < 10.
less_than_10([]).
less_than_10([Head|Tail]) :-
    Head < 10,
    less_than_10(Tail).

只是让你知道我的知识在哪里。 很基础。 我确实阅读了 Learn Prolog Now 中的列表章节,但它仍然让我感到困惑。 他们给了我们一个提示:

每个节点应呈现为:

delta(1, d, 2)
% or
alpha(2, a, 2)

他们还告诉我们将问题中的列表传递给谓词,如果列表适合自动机,则返回true ,否则返回false

accept([d,a,b,a,b,b,b,c,d,c]).

输出为true

然后去哪儿? 我猜第一步是检查列表的Head是否为1 我怎么做? 另外,我应该将每个节点作为事实添加到知识库中吗?

所以这很容易。 超级直接,比使用 C 或 Java 时要多得多。

让我们为此图编写一个解释器:

  • 给出一个命名转换的列表;
  • 使用给定图沿着通过该图的路径进行转换;
  • 如果我们最终处于最终状态,则接受(成功)列表;
  • 如果我们不这样做,则拒绝(失败)列表;
  • 并且.. 假设如果给定图表无法生成列表,则抛出异常。

如果有多个路径,Prolog 免费为我们提供不确定性。 这很好。

我们没有一个类来描述自动机。 从某种意义上说,Prolog 程序就是自动机。 我们只有一组谓词,它们通过归纳定义来描述自动机。 实际上,如果您在下面的源代码周围添加一个模块定义,那么您确实拥有该对象。

首先描述图表。 这只是一组 Prolog 事实。

根据需要,我们给出节点(用整数标记)之间的转换(用原子标记),另外我们指出哪些是开始节点和结束节点。 无需列出节点或边本身。

delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).

一个简单的数据库。 当然,这只是一种可能的表示。

现在是图表步行者。 我们可以在这里使用定句语法,因为我们正在处理一个列表,但我们不要。

首先,一个谓词“接受”或“拒绝”转换列表。

看起来像:

% accepts(+Transitions)

它以start state 开始,然后通过从列表中删除转换来“行走”,直到列表为空。 然后它检查它是否处于结束状态

accepts(Ts) :-               % accept the list of transitions if... 
   start(S),                 % you can accept the list starting 
   accepts_from(S,Ts).       % from a start state  
                       

accepts_from(S,[T|Ts]) :-    % accepts the transitions when at S if...
   delta(S,T,NextS),         % there is a transition S->NextS via T
   accepts_from(NextS,Ts).   % and you can accept the remaining Ts from NextS. (inductive definition)

accepts_from(S,[]) :-        % if there is no transition left, we accept if...
   end(S).                   % we are a final state

啊,如果该图的路径不可能,我们想抛出。 所以稍微修改一下:

accepts(Ts) :-               % accept the list of transitions if... 
   start(S),                 % you can accept the list starting 
   accepts_from(S,Ts).       % from a start state  
                       

accepts_from(S,[T|Ts]) :-    % accepts the transitions when at S if...
   delta(S,T,NextS),         % there is a transition S->NextS via T
   accepts_from(NextS,Ts).   % and you can accept the remaining Ts from NextS.

accepts_from(S,[T|Ts]) :-    % accepts the transitions when at S if...
   \+ delta(S,T,NextS),      % there is NO transition S->NextS via T
   format(string(Txt),"No transition at ~q to reach ~q",[S,[T|Ts]]),
   throw(Txt).

accepts_from(S,[]) :-        % if there is no transition left, we accept if...
   end(S).                   % we are a final state

所以:

?- accepts([d,a,b,a,b,b,b,c,d,c]).
true ;   % yup, accepts but maybe there are other paths?
false.   % nope

?- accepts([d,a,a,a,a,e]).
true ;
false.

?- accepts([d,a,a,a,a]).
false.

?- accepts([d,c,e,a]).
ERROR: Unhandled exception: "No transition at 3 to reach [e,a]"

上面的代码也应该能够通过图表找到可接受的路径。 但它没有:

?- accepts(T).
... infinite loop

这不好。

主要原因是accept/2将立即通过转换ab在状态2处生成无限路径循环。 所以需要添加一个“深度限制器”(关键字是“迭代加深”)。

第二个原因是测试\+ delta(S,T,NextS)将在节点4处成功(因为从该节点无处可去)并在尝试无处可去的可能性之前导致异常(最后一个条款)。 所以在生成时,投掷是一种障碍,一个人只想拒绝。

附录:同时生成

下面只接受/拒绝不抛出,但也可以生成。

:- use_module(library(clpfd)).

accepts(Ts,L) :-                 % Accept the list of transitions Ts of length L if
   start(S),                     % ...starting from a start state S
   accepts_from(S,Ts,L).         % ...you can accept the Ts of length L.                       

accepts_from(S,[T|Ts],L) :-      % Accept the transitions [T|Ts] when at S if
   (nonvar(L)
    -> L >= 1
    ;  true),                    % L (if it is bound) is at least 1 (this can be replaced by L #> 0)
   delta(S,T,SN),                % ...and there is a transition S->SN via T
   Lm #= L-1,                    % ...and the new length is **constrained to be** 1 less than the previous length
   accepts_from(SN,Ts,Lm).       % ...and you can accept the remaining Ts of length Lm from SN.

accepts_from(S,[],0) :-          % If there is no transition left, length L must be 0 and we accept if
   end(S).                       % ...we are a final state.

delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).

generate :-
   between(0,7,L),
   findall(Ts,accepts(Ts,L),Bag),
   length(Bag,BagLength),
   format("Found ~d paths of length ~d through the graph\n",[BagLength,L]),
   maplist({L}/[Ts]>>format("~d : ~q\n",[L,Ts]),Bag).

所以:

?- accepts([d,a,b,a,b,b,b,c,d,c],_).
true ;
false.

?- accepts([d,a,a,a,a],_).
false.

?- accepts([d,c,e,a],_).
false.

?- generate.
Found 0 paths of length 0 through the graph
true ;
Found 0 paths of length 1 through the graph
true ;
Found 2 paths of length 2 through the graph
2 : [d,d]
2 : [d,e]
true ;
Found 4 paths of length 3 through the graph
3 : [d,a,d]
3 : [d,a,e]
3 : [d,b,d]
3 : [d,b,e]
true ;
Found 9 paths of length 4 through the graph
4 : [d,a,a,d]
4 : [d,a,a,e]
4 : [d,a,b,d]
4 : [d,a,b,e]
4 : [d,b,a,d]
4 : [d,b,a,e]
4 : [d,b,b,d]
4 : [d,b,b,e]
4 : [d,c,d,c]
true 

这是我的答案。 我试图将数据与逻辑完全分开。 有一些规则可以推断可能的路径、开始和结束节点。

edge/2谓词代表 alpha 或 delta 线。 path (DCG) 谓词描述以结束节点结束的边列表。 使用start_node/1end_node/1谓词推断开始和结束节点。 最后, phrase/3用于描述有效自动机的路径列表。

delta(1, d, 2).
delta(2, d, 4).
delta(2, e, 5).
delta(2, c, 3).
delta(3, d, 6).
delta(6, c, 5).

alpha(2, a, 2).
alpha(2, b, 2).


edge(Node, Node, Via) :-
    alpha(Node, Via, Node).

edge(From, To, Via) :-
    delta(From, Via, To).


path(From, To) -->
    { end_node(To),
      dif(From, To),
      edge(From, To, Via)
    },
    [Via].

path(From, To) -->
    {edge(From, Mid, Via)},
    [Via],
    path(Mid, To).


start_node(Node) :-
    node_aux(start_node_aux, Node).

end_node(Node) :-
    node_aux(end_node_aux, Node).

start_node_aux(Node) :-
    edge(Node, _, _),
    \+ edge(_, Node, _).

node_aux(Goal, Node) :-
    setof(Node, call(Goal, Node), Nodes),
    member(Node, Nodes).

end_node_aux(Node) :-
    edge(_, Node, _),
    \+ edge(Node, _, _).


automaton -->
    {start_node(Start)},
    path(Start, _End).

accept(Steps) :-
    length(Steps, _N),
    phrase(automaton, Steps).

我怀疑 David 没有使用定从句语法,因为您在学习 DCG 之前应该熟悉基础知识。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM