Prolog DCG set_prolog_flag double_quotes源代码指令位置事项;文档?

时间:2017-07-29 13:54:57

标签: prolog directive dcg iso-prolog

我通过SWI-Prolog学到了很难的方法,Prolog指令set_prolog_flag的位置在源代码文件中很重要。

我发现有关使用指令加载源代码文件的唯一文档是Loading Prolog source files

  

指令是编译器的指令。指令习惯了   set(谓词)属性(参见4.15节),设置标志(见   set_prolog_flag / 2)并加载文件(本节)。指令是术语   形式: - < term>。

是否有SWI-Prolog的文档,其中包含源代码的加载,如果指令适用于整个文件或取决于源代码文件中的位置?

或者是从源代码文件加载的所有行只是简单地将语句播放到顶层并且位置始终很重要?

Supplement / TL; DR

默认

当在Prolog中使用明确的条款语法(DCG)时,已知DCG要求输入是字符代码列表,例如,

?- string_codes("abc123",Cs).
Cs = [97, 98, 99, 49, 50, 51].

并在源代码文件中使用以下DCG规则并加载到顶级

digit(0) --> "0".

DCG可与

一起使用
?- string_codes("0",Cs),phrase(digit(D),Cs,R).
Cs = [48],
D = 0,
R = []

set_prolog_flag

现在更容易使用DCG而不必使用string_codes Prolog指令

:- set_prolog_flag(double_quotes, chars).

可以在源代码文件中使用,并在源代码文件中使用以下DCG规则并加载到顶级

digit(0) --> "0".

DCG可与

一起使用
?- phrase(digit(D),"0",R).
D = 0,
R = [].

遗漏了一些重要的东西

事实证明,if set_prolog_flag出现之前 DCG规则,然后跳过string_codes,但如果set_prolog_flag出现 > DCG规则然后跳过string_codes失败。

:- set_prolog_flag(double_quotes, chars).
digit(0) --> "0".

?- phrase(digit(D),"0",R).
D = 0,
R = [].

VS

digit(0) --> "0".
:- set_prolog_flag(double_quotes, chars).

?- phrase(digit(D),"0",R).
false.

导致我发怒的原因

虽然我知道Prolog的大量编程只能在顶层完成,但我倾向于依赖源代码文件和consult/1。 在编写大量代码时,我开始使用modules。通过模块,我发现Prolog标志对于每个模块都是独立的。

?- current_prolog_flag(double_quotes,V).
V = string.

?- current_prolog_flag(symbolic:double_quotes,V).
V = string.

?- set_prolog_flag(symbolic:double_quotes,chars).
true.

?- current_prolog_flag(double_quotes,V).
V = string.

?- current_prolog_flag(symbolic:double_quotes,V).
V = chars.

并且默认的顶级模块是user

?- current_prolog_flag(double_quotes,V).
V = string.

?- current_prolog_flag(user:double_quotes,V).
V = string.

?- set_prolog_flag(double_quotes,chars).
true.

?- current_prolog_flag(double_quotes,V).
V = chars.

?- current_prolog_flag(user:double_quotes,V).
V = chars.

?- set_prolog_flag(user:double_quotes,codes).
true.

?- current_prolog_flag(double_quotes,V).
V = codes.

?- current_prolog_flag(user:double_quotes,V).
V = codes.

让我误认为Prolog指令set_prlog_flag适用于整个模块,无论它写在哪里。

什么打破了模具

在编写大量示例代码时,将所有小示例保存在一个文件中并与每个小示例关联的set_prolog_flag更容易。对于标识符示例,它需要两个小的示例DCG规则,一个用于数字,一个用于字母。数字规则高于字母规则和工作,但字母规则有set_prolog_flag指令,因为我当时正在处理它们。请记住,我认为该指令此时适用于整个文件。然后在测试ident时,字母的DCG规则正在运行,但数字的DCG规则失败了。

digit(0) --> "0", !.
digit(1) --> "1", !.
digit(2) --> "2", !.

:- set_prolog_flag(double_quotes, chars).

ident(Id) --> letter(C), identr(Cs), { name(Id, [C|Cs]) }.

identr([C|Cs]) --> letter(C), !, identr(Cs).
identr([C|Cs]) --> digit(C), !, identr(Cs).
identr([])     --> [].

letter(a) --> "a", !.
letter(b) --> "b", !.
letter(c) --> "c", !.

?- phrase(ident(Id),"ab12",R).
Id = ab,
R = ['1', '2'].

根本原因

所以使用listing/1

?- listing(digit).
digit(0, [48|B], A) :- !,
        A=B.
digit(1, [49|B], A) :- !,
        A=B.
digit(2, [50|B], A) :- !,
        A=B.


?- listing(ident).
ident(C, A, F) :-
        letter(D, A, B),
        identr(E, B, G),
        name(C, [D|E]),
        F=G.

?- listing(identr).
identr([A|D], B, F) :-
        letter(A, B, C), !,
        E=C,
        identr(D, E, F).
identr([A|D], B, F) :-
        digit(A, B, C), !,
        E=C,
        identr(D, E, F).
identr([], A, A).

?- listing(letter).
letter(a, [a|B], A) :- !,
        A=B.
letter(b, [b|B], A) :- !,
        A=B.
letter(c, [c|B], A) :- !,
        A=B.

问题很明显

digit(0, [48|B], A) :- !,
    A=B.

letter(a, [a|B], A) :- !,
        A=B.

该数字已转换为使用字符代码48,并且字母已转换为使用字符a。就在那时我问自己,set_prolog_flag在源代码中的位置是否重要。

确认根本原因

为了测试这个,我创建了一个小的源代码文件

digit_before(0) --> "0".

:- set_prolog_flag(double_quotes, chars).

digit_after(0) --> "0".

并在顶级

?- current_prolog_flag(double_quotes,V).
V = string.

?- current_prolog_flag(symbolic:double_quotes,V).
V = string.

?- consult("C:/Users/Eric/Documents/Projects/Calculus Project/test.pl").
true.

?- current_prolog_flag(double_quotes,V).
V = chars.

?- current_prolog_flag(symbolic:double_quotes,V).
V = string.

?- listing(digit_before).
digit_before(0, [48|A], A).

true.

?- listing(digit_after).
digit_after(0, ['0'|A], A).

true

确认Prolog指令set_prolog_flag不适用于整个文件。请注意,digit_before将转换为48,而digit_after将转换为'0'

备注

注意:指令set_prolog_flag(F,V)也可以在顶级使用,不需要前面的:-

注意:使用:- set_prolog_flag(double_quotes, chars).的示例,但:- set_prolog_flag(double_quotes, codes).也有效。首选使用chars值是因为它在调试等时使值更容易阅读

2 个答案:

答案 0 :(得分:2)

我可以说你可以确保set_prolog_flag(double_quotes, chars)指令具有所需的行为(适用于整个文件)。

这可以通过initialization/2.指令使用选项after_load或使用initialization/1.

来完成
digit_before(0) --> "0".

:- initialization( set_prolog_flag(double_quotes, chars),  after_load ).

digit_after(0) --> "0".

SWI-Prolog initializаtion/2 directive

SWI-Prolog initializаtion/1 directive

关于如何向SWI-Prolog社区提出您的想法的问题,我希望(初始)解决方案存在第二个答案。

有用的链接:

Research papers by Ulrich Neumerkel and Fred Mesnard

Home Page of Markus Triska

包含大量专用于编程语言Prolog的不同材料。

答案 1 :(得分:2)

在SWI-Prolog中,指令和条款按顺序处理。 Prolog标志很复杂。总体规则是它们是线程范围的,其中子线程使用写时复制语义共享来自其创建者的标志,这实际上与除了性能和内存使用之外将复制所有标志时相同。但是,一些标志的范围限定在它们出现的源文件中。这意味着load_files / 2在加载之前保存标志的状态并在之后恢复它。其他一些标志是模块作用域,这意味着标志API仅仅是更改模块属性的代理。这些标志不是特定于线程的,因为模块是全局的。另请注意,某些标志会影响读取(例如double_quotes),而其他标志会影响编译器(optimise),并且大多数会影响运行时行为。

理想情况下,current_prolog_flag/2的文档应记录这些方面。不确定此文档是否准确。对于double_quotes,它会为每个模块维护