有没有一个可以绘制Prolog查询搜索树的程序?

11

我想知道是否存在一种工具,可以绘制Prolog程序的逐步搜索树?谢谢。

4个回答

14

如果您的Prolog系统具有可自定义的调试器,则可以轻松编写自己的运行时图收集代码。假设您的Prolog系统具有回调钩子goal_tracing/2,如Jekejeke Prolog中所示。然后,我们可以继续检查当前帧和父帧以创建图中的链接。以下是代码:

goal_tracing(call, F) :-
    frame_property(F, sys_call_indicator(N, A)),
    frame_property(F, sys_parent_frame(G)),
    frame_property(G, sys_call_indicator(M, B)),
    !,
    update_link(N / A, M / B).
goal_tracing(_, _).

:- dynamic link/2.
update_link(A, B) :-
    link(A, B),
    !.
update_link(A, B) :-
    assertz(link(A, B)).

可以看到,我们只检查调用端口并且只查看谓词指示器。但是也有其他可能收集更多数据的方法。现在我们需要一些实用程序来显示结果。在收集之前只需要调用重置,然后在收集之后调用显示:

reset :-
    retract(link(_, _)), fail.
reset.

show :-
    write('http://yuml.me/diagram/scruffy/class/'),
    link(A, B),
    write(([B] -> [A])),
    write(', '),
    fail.
show.

我们生成一个可以被 yuml.me 理解的链接。 让我们尝试一下 peano 阶乘程序。程序代码如下:
add(n, X, X).
add(s(X), Y, Z) :-
    add(X, s(Y), Z).

mul(n, _, n).
mul(s(X), Y, Z) :-
    mul(X, Y, H),
    add(Y, H, Z).

fac(n, s(n)).
fac(s(X), Y) :-
    fac(X, H),
    mul(s(X), H, Y).

我们可以按照以下方式运行收集器:
?- reset.
?- trace.
?- fac(s(s(n)),X).
X = s(s(n))
?- nodebug.
?- show.
http://yuml.me/diagram/scruffy/class/[fac / 2] -> [fac / 2], [fac / 2] -> [mul / 3], [mul / 3] -> [mul / 3], [mul / 3] -> [add / 3], [add / 3] -> [add / 3], Yes

然后,您可以将URL粘贴到浏览器中,就会看到图表。请删除URL末尾的“,Yes”。这是结果:

Call Graph

最好的祝福


你太棒了。我无话可说。 - IDDQD
可能是我在 SO 上读过的最好的答案之一。感谢 @j4n bur53。 - Will
1
它有点过时了,frame_property/2 目标需要升级。 - user502187
1
我有什么遗漏吗?问题是关于绘制树的。在我的理解中,它意味着展开递归,但所呈现的图形不是一棵树。 - user1700890

3

Prolog搜索树通常太大,无法一步一步地检查,但绘制它可能相当简单且有趣。也许我会尝试使用html_write库编写一个搜索树。在这种情况下,我会报告结果。

与此同时,SWI-Prolog在其调试器中具有相当奇特的表示方式。有关Prolog程序树的有趣细节。它不是很容易使用,我必须承认我仍然没有阅读文档。但是我经常使用调试器。您可以浏览树和各个节点上的已实例化变量。那是强大的。

可视化Prolog搜索空间是一项不简单的有趣任务!

编辑我忘记提到XPCE具有显示大型树的功能。如果您已经拥有证明树,将其显示应该非常容易。只需打开查看器即可。XPCE手册帮助中应该有一些示例。您可以基于此进行显示。


@Robert Oschler:非常感谢您纠正我表达不清的帖子。虽然很有趣,但我担心我会继续表达得很差。但我很感激。 - CapelliC
非常愉快,尤其是因为所包含的信息很重要。另外,向一位Prolog爱好者问好! - Robert Oschler

2
我用不同的方法解决了这个问题... 可以看看这个链接: https://github.com/reahaas/prolog-trace-to-tree 我在prolog中运行程序并跟踪,输出跟踪结果为文本。每一步都在不同的行上。 将此跟踪输出保存到文件中。它应该像这样:
?- trace,there_is_way(telaviv,heifa).
Call: (9) there_is_way(telaviv, heifa) ? creep
Call: (10) there_is_way(telaviv, heifa, nil) ? creep
Call: (11) road_from(telaviv, heifa) ? creep
Call: (12) road(telaviv, heifa) ? creep
Fail: (12) road(telaviv, heifa) ? creep
Fail: (11) road_from(telaviv, heifa) ? creep
Redo: (10) there_is_way(telaviv, heifa, nil) ? creep
Call: (11) road_from(telaviv, _4236) ? creep
Call: (12) road(telaviv, _4236) ? creep

然后使用这段Python代码打印调用跟踪树: 它依赖于跟踪的第一个单词来构建树:{Call,Fail,Exit,Redo}。
注意:在代码中更改文件路径/名称(使用 open(...))。
from pptree import *
import os


def get_first_word(line):
    if line is "":
        return
    words = line.split()
    if len(words) > 0:
        first_word = words[0]
        return first_word


def add_node(current, line):
    if current is None:
        return Node("head" + line, None)
    else:
        return Node(line, current)


with open("/home/vagrant/openu/prolog/trace_monkey.txt", 'r') as trace_file:
    current = None

    while True:
        line = trace_file.readline()
        if line.strip() == "":  # run till it face an empty string.
            break
        first_word = get_first_word(line)
        if current is None:
            call_tree = add_node(current, line)
            current = call_tree
        elif first_word == "Call:":
            current = add_node(current, line)
        elif first_word == "Exit:":
            add_node(current, line)  # get_assignment(line))
            current = current.parent
        elif first_word == "Fail:":
            add_node(current, line)
            current = current.parent
        elif first_word == "Redo:":
            current = add_node(current, line)

print_tree(call_tree)

这是最初的回答:
要查看结果,请将文本树粘贴到Notepad ++中并缩小视图 :) enter image description here

2
请看swi-prolog的sldnfdraw,它像魔法一样好用。唯一的问题是,其中的术语不能包含下划线,但我已经向作者发送了电子邮件报告此问题。它创建一个树形表示的tex文件,然后通过一些bash命令将其转换为png以进行可视化。
latex file.tex
dvipdf file.dvi
pdfcrop file.pdf
pdftoppm file-crop.pdf|pnmtopng > file.png

我建议添加 \usepackage[landscape]{geometry} 以为树提供额外的空间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接