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

问题描述 投票:0回答:4

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

prolog swi-prolog search-tree
4个回答
14
投票

如果您的 Prolog 系统有可定制的调试器,您可以轻松地 编写您自己的运行时图形收集代码。假设你的 Prolog 系统有一个回调钩子 goal_tracing/2 就像 Jekejeke 中一样 序言。然后我们可以继续检查当前帧和 父框架以在图表中创建链接。这是代码:

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 理解的链接。 让我们尝试一下皮亚诺阶乘程序。程序代码看起来 如下:

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 的末尾。这是结果:

Call Graph

致以诚挚的问候


3
投票

Prolog 搜索树通常太大而无法逐步检查,但绘制一个搜索树可能相当简单,也很有趣。也许我会尝试使用 html_write 库编写一个。那样的话,我会报告结果。

与此同时,SWI-Prolog 在其调试器中具有相当奇特的表现形式。关于 Prolog 程序树有一些有趣的细节。它并不那么容易使用,我必须承认我还没有阅读文档。 不过我经常使用调试器。您可以导航树和各个节点上的实例化变量。真是强大

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

edit 我忘了提及 XPCE 具有显示大树的能力。如果您已经拥有证明树,那么显示它应该非常容易。只需打开查看器即可。 XPCE手册帮助中应该有一些例子。您可以以此为基础进行显示。


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}
为树提供额外的空间。


2
投票

我用不同的方式解决了它...... 看看: https://github.com/reahaas/prolog-trace-to-tree

我在序言中使用跟踪运行程序,这给了我跟踪的文本输出。每个步骤都在不同的行中。 将此跟踪输出保存到文件中。它应该看起来像这样:

?- 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)

这是结果:

要查看结果,请将文本树粘贴到记事本++并缩小:) enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.