现代 Prolog 编译器是不是会在安全的情况下自动优化发生检查?

Posted

技术标签:

【中文标题】现代 Prolog 编译器是不是会在安全的情况下自动优化发生检查?【英文标题】:Do modern Prolog compilers optimize away the occurs check automatically, when it's safe?现代 Prolog 编译器是否会在安全的情况下自动优化发生检查? 【发布时间】:2021-03-24 15:41:01 【问题描述】:

编辑: 这个问题假设您启用了发生检查。 不是关于setting Prolog flags。

30 年前有一堆论文关于在安全的情况下自动优化发生检查(大约 90% 的谓词,在典型的代码库中)。提出了不同的算法。现代 Prolog 编译器(例如 SWI Prolog)是否会做类似的事情(当发生检查打开时)?他们喜欢什么算法?

例如,他们会在编译这样的谓词时删除发生检查:

less(0, s(_)).
less(s(X), s(Y)) :- less(X, Y).

(来自下面的讨论this answer)

【问题讨论】:

SWI-Prolog 不会自行更改 ISO 标志。在plunit library docs,sto(Terms) 部分,建议See 7.3.3 of ISO/IEC 13211-1 PROLOG: Part 1 - General Core, for a detailed discussion of STO and NSTO 【参考方案1】:

当发生检查标志设置为“true”时,有多种优化可以优化发生检查。这对于使健全的统一适用于常见情况是必要的。一个典型的优化是检测头部的线性度:

linear(Head) :-
   term_variables(Head, Vars),
   term_singletons(Head, Singletons),
   Vars == Singletons.

在这种情况下,调用子句时可以省略发生检查。我们可以对less/2 示例进行测试,以确定头部是否是线性的。事实证明,两个头都是线性的:

?- linear(less(0, s(_))).
true.

?- linear(less(s(X), s(Y))).
true.

因此,Prolog 系统可以完全省略谓词 less/2 的发生检查,但仍会产生合理的统一。例如,在检查中间代码时,可以在 Jekejeke Prolog 中看到这种优化。使用指令unify_linear:

?- vm_list(less/2).

less(0, s(A)).
  0  unify_linear _0, 0
  1  unify_linear _1, s(A)
less(s(X), s(Y)) :-
   less(X, Y).
  0  unify_linear _0, s(X)
  1  unify_linear _1, s(Y)
  2  goal_last less(X, Y)

与指令 unify_term 相比,指令 unify_linear 不执行发生检查,即使发生检查标志设置为真。

丽图查达;大卫 A. Plaisted (1994)。 “在序言中没有发生检查的统一正确性”。逻辑编程杂志。 18(2):99-122。 doi:10.1016/0743-1066(94)90048-5.

【讨论】:

【参考方案2】:

除非您明确要求,否则 SWI Prolog 不会打开发生检查:

通过在本地使用unify_with_occurs_check/2 而不是=。 (请注意,这意味着头部统一不受影响,即仍然在没有发生检查的情况下运行 - 我认为?) 通过开启检查全局 thorugh 设置标志occurs_check:set_prolog_flag(occurs_check,true)

这是个问题吗?我不这么认为。

考虑在其他编程语言中使用断言的相关案例(甚至在 Prolog 中:assertion/1,我衷心推荐它)。

当您进行开发时,您将“开启”这些功能,以在运行时以一定的 CPU 成本验证约束。一旦您确定您的程序有效(通过构建、配置和测试),您将“关闭它们”。您的程序与任何调用者(可能是恶意的、混淆的或有问题的)之间的接口仍可能受到must_be/2 检查的保护,以便强制执行接口协定。

发生检查的情况类似:如果您怀疑循环数据结构可能会出现并导致问题,请打开“发生检查”。您编写的代码使一切正常(并且您确定它正常工作,最好通过构造和证明)。然后再关掉。

【讨论】:

问题假设发生检查是打开的,当然(否则,没有什么可以删除):“(当发生检查打开时)?” @MaxB Aie... 来晚了。【参考方案3】:

什么也流行,使用变量内部的区别,如“新鲜”和“陈旧”变量。 “新鲜”变量通常不需要发生检查。

可以动态进行区分。下面是一些结果,动态区分(“Freshness”)也可以结合一些静态分析(“Linear”):

上图显示了基于引用计数的启发式结果。下面的论文讨论了使用一位,即将变量标记为 UNBOUND 或 NEW_UNBOUND。

再访 Occur-Check 问题 - Beer,1988 年https://core.ac.uk/download/pdf/82747799.pdf

【讨论】:

以上是关于现代 Prolog 编译器是不是会在安全的情况下自动优化发生检查?的主要内容,如果未能解决你的问题,请参考以下文章

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

Java的嵌入式Prolog解释器/编译器

什么是现代的、可移植的、安全的等价于 C 中编译时检查的 memcpy?

Prolog/ASP(Clingo) 到 CLIPS 翻译器

七个Swift中的陷阱以及避免方法