20

ISO-Prolog(ISO/IEC 13211-1:1995 包括 Cor.1:2007、Cor.2:2012)提供以下内置谓词来测试术语的类型:

8.3 型式试验

1 var/1。2个原子/1。3 整数/1。4个浮子/1。5个原子/1。6化合物/1。7 无变量/1。8个数字/1。9个可调用/1。10地/1。11 非循环术语/1。

在这一组中,有些人的目的仅仅是测试某个实例化,即 8.3.1 var/1、 8.3.7 nonvar/1、 8.3.10 ground/1,以及那些假设一个术语已充分实例化以使型式测试是安全的。不幸的是,它们与具体实例化的测试相结合。

考虑如果是非整数的非变量项并且何时是变量,则integer(X)失败的目标。这会破坏许多理想的声明性属性:XX

?- X = 1, integer(X).
true.

?- integer(X), X = 1.
false.

理想情况下,第二个查询要么使用某种形式的协程成功;要么 否则它会根据错误分类发出实例化错误1 ​​。毕竟:

7.12.2 错误分类

错误按照Error_term的形式分类:


a) 当参数或其组件之一是变量并且需要
实例化参数或组件时,应存在实例化错误。它有
形式instantiation_error

...

请注意,实例化测试和类型测试的这种隐式组合会导致 Prolog 程序以及 SO 中的许多错误。

解决这种情况的一个快速方法是在每个内置测试之前添加一个显式测试,或者详细地为

   ( nonvar(T) -> true ; throw(error(instantiation_error,_)) ),
   integer(T), ....

或更紧凑地为

functor(T, _,_),
integer(T), ....

甚至可能

T =.. _,
integer(T), ...

我的问题是双重的:

如何在用户级别提供此功能?

而且,为了使这也有点挑战性:

atomic/1用 ISO-Prolog 编写的更安全的最紧凑的实现是什么?


1 其他不太理想的选择是循环或产生资源错误。仍然比不正确的结果更可取。

4

2 回答 2

7

类型测试需要将自己与传统的“类型测试”内置函数区分开来,后者隐式地也测试足够的实例化。因此,我们仅有效地测试充分实例化的术语 ( si)。如果它们没有充分实例化,则会发出适当的错误。

对于一个类型,因此存在一个带有唯一错误条件nn的类型测试谓词。nn_si/1

a) 如果存在一个 θ 和 σ 使得它nn_si(Xθ)是真的和nn_si(Xσ)是假的
- instantiation_error

atom_si(A) :-
   functor(A, _, 0),    % for the instantiation error
   atom(A).

integer_si(I) :-
   functor(I, _, 0),
   integer(I).

atomic_si(AC) :-
   functor(AC,_,0).

list_si(L) :-
   \+ \+ length(L, _),  % for silent failure
   sort(L, _).          % for the instantiation error

library(si)在 Scryer 中可用。

在 SWI 中,由于其在 中的不同行为length/2,请使用:

list_si(L) :-
    '$skip_list'(_, L, T),
    functor(T,_,_),
    T == [].
于 2015-06-02T15:18:53.120 回答
2

这是实施您建议的两种解决方案的非常天真的尝试。

首先,has_type(Type, Var)成功或失败并出现实例化错误:

has_type(Type, X) :-
    var(X), !,
    throw(error(instantiation_error, _)).
has_type(Type, X) :-
    nonvar_has_type(Type, X).

nonvar_has_type(atom, X) :- atom(X).
nonvar_has_type(integer, X) :- integer(X).
nonvar_has_type(compound, X) :- compound(X).
% etc

其次,一个could_be(Type, Var)(类似于must_be/2)使用协程允许查询在将来的某个时间点成功:

could_be(Type, X) :-
    var(X), !,
    freeze_type(Type, X).
could_be(Type, X) :-
    nonvar_has_type(Type, X).

freeze_type(integer, X) :- freeze(X, integer(X)).
freeze_type(atom, X) :- freeze(X, atom(X)).
freeze_type(compound, X) :- freeze(X, compound(X)).
% etc

这种方法有几个弱点,但您的评论可能会帮助我更好地理解用例。

编辑:关于 Prolog 中的“类型”

据我了解,Prolog 中的类型不是“类型”:它们只是可以在运行时查询的信息,之所以存在是因为它是底层实现的有用的泄漏抽象。

我能够实际使用“类型”的唯一方法是“标记”我的变量,如复合术语number(1)number(pi)operator(+)date(2015, 1, 8)等等。然后我可以将变量放在那里,编写确定性或半确定性谓词,一周后看到我的代码时理解它的含义......

所以自由变量和整数只是术语;主要是因为,正如您的问题非常巧妙地指出的那样,自由变量可以成为整数、原子或复合词。您可以使用协同程序来确保自由变量以后只能成为某种“类型”的术语,但是从实际的角度来看,这仍然不如使用复合术语。

我很可能在这里混淆了非常不同的问题;老实说,我对 Prolog 的体验充其量是有限的。我刚刚阅读了我正在使用的实现的文档,并尝试找出使用它的最佳方法。

于 2015-01-08T13:31:48.130 回答