我想将DCG用作发电机。截至目前,语法为
s-->a,b.
a-->[].
a-->a,c.
c-->[t1].
c-->[t2].
b-->[t3].
b-->[t4].
我想生成s
的{{1}}长度为a
的所有< someNumber
。
使用?- phrase(a,X),length(X,Y),Y<4.
我可以获得少于4件物品的所有a
。但是,当所有组合都用尽时,系统(SWI-Prolog 6.2.5)似乎停滞不前。
有时候,一个类似的问题是asked here。但是,作为Prolog的新手,我无法使用上面的语法。有什么想法吗?
更新:(canrememberthename)发表评论,不知何故被删除了。无论如何,有人建议使用between(1,4,Y),length(X,Y),phrase(a,X).
来设置限制。将代码更改为a-->c,a.
答案 0 :(得分:5)
Prolog的第一步往往有点困难。但是你走在正确的轨道上:
目前,您的问题是您得到了一些预期的答案/解决方案,但系统停滞不前;事实上,它处于无限循环中。你通过耐心地击中 SPACE 找到了。这可能适用于一小部分解决方案,但如果使用较大的解决方案则会很繁琐。想想看看所有短于20的句子。
有一个简单的键盘和腕管友好的方式来模拟击球空间:只需在最后添加一个目标 false
:
?- phrase(a,X),length(X,Y),Y<4, false.
Prolog能回答这个问题吗?由于最后有 false
,Prolog没有太多选择:它可以回答false
本身;或者它循环(或产生错误或产生一些副作用)。在这种情况下,它循环。
现在,我们可以通过在您的计划中添加更多 false
目标来缩小问题范围。
?- phrase(a,X),length(X,Y),false, Y<4, false. **LOOPS**
为了更好地理解这一点,我只会使用一个 false
目标并浏览查询的其余部分:
?- phrase(a,X),length(X,Y),false,Y<4. **LOOPS**
让我们进一步减少:
?- phrase(a,X),false,length(X,Y),Y<4. **LOOPS**
他们全都循环!但是我们得到了一些有趣的见解:由于这些 false
多条查询不会终止,原始程序也不会终止。因此,当您查看查询,并且第一个目标不会自行终止时,整个查询将不会终止(请参阅最后的细则以获取更多信息)。
因此:你必须以某种方式解决第一个目标 !
我的第一次尝试是交换length
和phrase
:
?- length(X,Y), phrase(a,X), Y<4.
这会有用吗?看看循环的第一个目标:
?- length(X,Y), false,phrase(a,X), Y<4.
所以这又不会终止。
您必须再次更改程序:
?- between(1,3,Y), length(X,Y), false,phrase(a,X). false.
所以这终止了。如果出现终止问题,phrase(a,X)
现在应该承担责任:
?- between(1,3,Y), length(X,Y), phrase(a,X), false. **LOOPS**
你可能想看看实际的答案:
?- between(1,3,Y), length(X,Y), phrase(a,X). Y = 1, X = [t1] ; Y = 1, X = [t2] ; ERROR: Out of local stack
你可能会得出结论,这种行为比你原来的定义更糟糕。毕竟,我们现在有少的答案。但确切地说,这种推理并不能帮助您改善终止:使用 false
两者都有同样的错误,您必须解决此问题第一即可。因此,通过隐藏答案,您可以更好地专注于其余部分。
但为什么你的语法有问题呢?我们可以继续使用我们的技术来插入目标 false
来缩小范围。但是这次你的语法。您使用 false
目标装饰的程序称为failure-slice。
只是一个实用的评论:当我在你的程序中插入false时,我将保存程序并在SWI中输入
make
:这样就可以快速重新编译程序。
尝试了一下之后,我得到了以下最小的失败切片。请注意,在DCG中, false
必须写为 {false}
。
?- between(1,3,Y), length(X,Y), phrase(a,X), falses--> {false}, a,b.a-->[], {false}. a-->a,{false},c.c-->{false}, [t1].c-->{false}, [t2].b-->{false}, [t3].b-->{false}, [t4].
几乎所有代码库都属于false
!所以你必须解决微小的可见剩余部分。在其他地方改变一些东西是毫无意义的。必须更改此a --> a, ...
!事实上,将其更改为a --> c, s
可以解决问题。
为什么你首先写a --> a, c.
?我怀疑你想以公平的方式列举所有解决方案。新版本没有:
?- phrase(a,X). X = [] ; X = [t1] ; X = [t1, t1] ; X = [t1, t1, t1] ; X = [t1, t1, t1, t1] ; X = [t1, t1, t1, t1, t1] ; X = [t1, t1, t1, t1, t1, t1] ; X = [t1, t1, t1, t1, t1, t1, t1] ...
这看起来非常令人生畏。事实上,它看起来是错误的。不是吗?但是不要让你混淆:我们这里有无限的句子。所以Prolog唯一正确的回答是产生无限多的答案。因为,如果它们是有限的,一些列表将会丢失!但是,当然,你希望以公平的方式列举它们。要做到这一点,只需写下:
?- length(X,N), phrase(a,X). X = [], N = 0 ; X = [t1], N = 1 ; X = [t2], N = 1 ; X = [t1, t1], N = 2 ; X = [t1, t2], N = 2 ; X = [t2, t1], N = 2 ; X = [t2, t2], N = 2 ; X = [t1, t1, t1], N = 3 ...
这是Prolog计划的一个重点:始终首先寻求最佳(可能)终止财产。并且不要看Prolog如何列举答案的准确顺序。因为,如果一个程序具有更好的终止属性,那么使用它以公平的方式枚举所有解决方案是非常重要的。但是:以更公平的方式列举无限多个解决方案而牺牲终止的程序不能用于更有趣的案例。
精细打印
See this answer。
答案 1 :(得分:2)
非终结符a // 0 左递归和&#39; epsilon&#39; (生成空序列),短语/ 2将在空制作后立即循环。
您可以解决问题边界列表&#39;长度:
?- between(1,4,Y),length(X,Y),phrase(a,X).
并且,正如您已经完成的那样,删除左递归。