Prolog DCG set_prolog_flag double_quotes源代码指令的位置很重要;文档



我从中了解到,使用SWI-Prolog,Prolog指令的位置在源代码文件中set_prolog_flag重要。

我发现关于使用指令加载源代码文件的唯一有价值的文档是在加载Prolog源文件中

指令

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

是否有SWI-Prolog的文档涵盖了源代码的加载,这些文档记录了指令是适用于整个文件还是取决于源代码文件中的位置?

还是从源代码文件加载的所有行都只是将语句简单地播放到顶层,位置总是很重要?

补充/TL;博士

违约

在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 更容易,而不必使用 Prolog 指令string_codes

:- set_prolog_flag(double_quotes, chars).

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

digit(0) --> "0".

DCG 可用于

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

这遗漏了一些重要的东西

事实证明,如果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 = [].

digit(0) --> "0".
:- set_prolog_flag(double_quotes, chars).
?- phrase(digit(D),"0",R).
false.

导致我犯规的推理

虽然我知道Prolog的很多编程都可以在顶层完成,但我倾向于依赖源代码文件并咨询/1。
在编写大量代码时,我开始使用模块。对于模块,我发现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'].

根源

所以使用列表/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值,因为它使调试等时更易于读取值。

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

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

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

这可以通过使用带有选项after_load的指令initialization/2.来完成,或者通过使用initialization/1.

digit_before(0) --> "0".
:- initialization( set_prolog_flag(double_quotes, chars),  after_load ).
digit_after(0) --> "0".

SWI-Prolog initializаtion/2 指令

SWI-Prolog initializаtion/1 指令

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

有用的链接:

Ulrich Neumerkel和Fred Mesnard的研究论文

马库斯·特里斯卡的主页

包含大量专门针对编程语言Prolog的各种材料。

最新更新