我正在编写一个DCG,它会采用u2v
形式的字符串,其中u
和v
不必具有相等的长度和{{{ {}}中的{1}}必须与0
中u
的数量相同。
到目前为止,我已经能够编写几个在纸上工作的语法,但是当我编写它们并尝试查询时,我通常会在某个地方找到一个循环。这是我能得到的最接近的:
1
我可以获得查询v
的正确答案:
s-->[2].
s-->u,[0],n.
s-->u,s,v.
n-->s,[1],v.
u-->[1],u.
u-->[].
v-->[0],v.
v-->[].
但它不会在s([0,1,1,2,0,0,1,0],N).
之后终止。相反,它只是重复这些答案。我对Prolog的了解非常有限,而且我主要是从LPN自学,所以我猜这更像是一个让我无法理解Prolog如何评估这些事情的问题。如果有人能解释为什么它不会终止,那将是最有帮助的。
答案 0 :(得分:2)
多么有趣的问题!
我想到的是你想要利用你的语法结构。例如,要匹配括号,您必须按照以下方式执行某些操作:
expr --> [].
expr --> ['('], expr, [')'].
这样,你可以在parens之间找到任何东西,或者你每次都有一个左和一个正确的paren。
我没有看到用这个问题实现这种崩溃的明显方法。可能有办法,但我不知道它是什么。然而,由于Prolog的变量工作方式,DCG具有几乎超自然的能力在解析中的不同位置之间传播信息,并且通过为DCG规则创建参数,如下所示:
rule(Stuff) --> ...
无论如何,下一个问题是你想要计算某些东西,这意味着可能涉及算术。然而!我们可以通过使用Peano数字来欺骗一点,因为我们真正关心的是它们在两边碰巧彼此相等,我们并不需要将结果传递给用户。
不用多说,这就是我提出的解决方案:
u2v --> u2v(_).
u2v(N) --> u(N), [2], v(N).
u(0) --> [].
u(s(N)) --> [0], u(N).
u(N) --> [X], { member(X, [1,2,3,4,5,6,7,8,9]) }, u(N).
v(0) --> [].
v(s(N)) --> [1], v(N).
v(N) --> [X], { member(X, [0,2,3,4,5,6,7,8,9]) }, v(N).
我确信有更好的方法来编写这个代码(甚至可能有完全优越的方法),但这个似乎在我的有限测试中起作用。 会甚至生成,除了双方的讨厌的无界递归。
解决方案的关键是这一行:
u2v(N) --> u(N), [2], v(N).
实际上,您不需要保留N
,但我发现它对调试很有用。这里必不可少的是u(N)
匹配v(N)
。然后u//1
和v//1
具有相同的结构:首先,匹配任何内容的基本情况,然后是与所需数字匹配的归纳情况并递增计数,Peano风格(0,s( 0),s(s(0)),s(s(s(0))),...),然后是一个与其他数字匹配的备用归纳案例,并向前传播前一个计数。
有可能用succ/2
而不是这个来做这件事,但是我担心在哪里需要放置一些纯Prolog才能让它成为现实。和Peano一起走这条路似乎对我来说更简单。 (甚至可能采用clpfd方法。)
无论如何,我希望这有帮助!
答案 1 :(得分:1)
在我看来,OP假设输入中只出现0s,1s和2s。在这个假设下,这是Daniel Lyons的建议的变体:
u2v --> u(N),[2],v(N).
u(0) --> [].
u(N) --> [1], u(N).
u(N) --> [0], u(N1), { N1 #= N - 1 }.
v(0) --> [].
v(N) --> [0], v(N).
v(N) --> [1], v(N1), { N1 #= N - 1 }.
OP的测试用例在SWI-Prolog 7.6.4中产生了这个:
?- phrase(u2v, [0,1,1,2,0,0,1,0]).
true ;
false.
我承认我太新了,不明白为什么我不会'真实'。结果。