0

我试图找到一种在变量绑定到值之前设置变量类型的方法。不幸的是,integer/1谓词不能用于此目的:

%This goal fails if Int is an unbound variable.
get_first_int(Int,List) :-
    integer(Int),member(Int,List),writeln(Int).

我写了一个谓词is_int,尝试提前检查类型,但它没有按我预期的那样工作。它允许将变量绑定到原子而不是整数:

:- initialization(main).
%This prints 'a' instead of 1.
main :- get_first_int(Int,[a,b,c,1]),writeln(Int).

get_first_int(Int,List) :-
    is_integer(Int),member(Int,List).

is_integer(A) :- integer(A);var(A).

是否仍然可以设置尚未绑定到值的变量的类型?

4

2 回答 2

3

在 SWI-Prolog 中,我曾用于when/2类似情况。我真的不知道这是不是一个好主意,它绝对感觉像一个黑客,但我想如果你只想说“这个变量只能变成 X”就足够了 X 是integer, or number, oratom等​​等上。

所以:

will_be_integer(X) :- when(nonvar(X), integer(X)).

接着:

?- will_be_integer(X), member(X, [a,b,c,1]).
X = 1.

但我有一种感觉,你几乎总能找到一种不那么老套的方法来实现同样的目标。例如,为什么不直接写:

?- member(X, [a,b,c,1]), integer(X).

???

于 2017-05-01T18:36:28.427 回答
2

整数的特定约束

除了鲍里斯所说的,我对整数的特殊情况有一个建议:考虑使用CLP(FD) 约束来表示变量必须是整数类型 。为了仅表达这个非常普遍的要求,您可以发布一个 CLP(FD) 约束,该约束必须适用于所有整数。

例如:

?- inf..sup 中的 X。
X inf..sup.

从此时起,X只能实例化为整数其他一切都会产生类型错误

例如:

?- inf..sup 中的 X,X = 3。X
 = 3。

?- inf..sup 中的 X,X = a。
错误:类型错误:预期为“整数”,找到“a”(原子)

声明式地,您始终可以用静默失败替换类型错误,因为如果出现此错误,任何可能的附加实例都无法使程序成功

因此,如果您更喜欢静默失败而不是这种类型的错误,您可以通过以下方式获得它 catch/3

?- inf..sup 中的 X,catch (X = a, error(type_error(integer,_),_) , false)。
错误的。

CLP(FD) 约束是为整数量身定制的,让您还可以方便地表达对该特定域的进一步要求。

针对具体案例的建议

让我们考虑一下您的具体示例get_first_int/2。首先,让我们重命名它,list_first_integer/3以便清楚每个参数是什么,并表明我们完全打算在多个方向上使用它,不仅仅是为了“获取”,还为了测试和理想地生成列表和整数在这种关系中。

其次,注意这个谓词相当混乱,因为它不纯粹地依赖于列表和整数的实例化,这个属性不能在一阶逻辑中表达,而是依赖于这个逻辑之外的东西。如果我们接受这一点,那么一种非常直接的方式来做你主要想要的事情就是把它写成:

list_first_integer(Ls, I) :-
        一次((成员(I0,Ls),整数(I0))),
        我 = I0。

只要列表被充分实例化,这就会起作用,这在您的示例中隐含地似乎是这种情况,但绝对不需要是一般情况。例如,对于完全实例化的列表,我们得到:

?- list_first_integer([a,b,c], I)。
错误的。

?- list_first_integer([a,b,c,4], I)。
我 = 4。

?- list_first_integer([a,b,c,4], 3)。
错误的。

相反,如果列表没有充分实例化,那么我们有以下主要问题:

?- list_first_integer(Ls, I)。
不终止

并进一步:

?- list_first_integer([X,Y,Z], I)。
错误的。

即使更具体的实例化成功

?- X = 0 , list_first_integer([X,Y,Z], I)。
X = 我,我 = 0。

核心问题:默认表示

核心问题是您在这里推理默认术语:仍然是变量的列表元素可能被实例化为整数将来的任何其他术语。一个干净的出路是设计你的数据表示来象征性地区分可能的情况。例如,让我们使用包装器 i/1来表示整数,并o/1表示任何其他类型的术语。有了这个表示,我们可以写:

list_first_integer([i(I)|_], I)。
list_first_integer([o(_)|Ls], I) :-
        list_first_integer(Ls, I)。

现在,我们得到正确的结果:

?- list_first_integer([X,Y,Z], I)。
X = i(I) ;
X = o(_12702),
Y = i(I) ;
X = o(_12702),
Y = o(_12706),
Z = i(I) ;
错误的。

?- X = i(0), list_first_integer([X,Y,Z], I)。
X = i(0),
我 = 0 ;
错误的。

如果我们只使用干净的数据表示,其他示例也仍然有效:

?- list_first_integer([o(a),o(b),o(c)], I)。
错误的。

?- list_first_integer([o(a),o(b),o(c),i(4)], I)。
我 = 4 ;
错误的。

?- list_first_integer([o(a),o(b),o(c),i(4)], 3)。
错误的。

最一般的查询现在允许我们生成解决方案:

?- list_first_integer(Ls, I)。
Ls = [i(I)|_16880] ;
Ls = [o(_16884), i(I)|_16890] ;
ls = [o(_16884), o(_16894), i(I)|_16900] ;
ls = [o(_16884), o(_16894), o(_16904), i(I)|_16910] ;
等等

您必须为这种普遍性付出的代价在于这些符号包装。由于您似乎关心代码的正确性和通用性,因此与更容易出错的默认方法相比,我认为这很划算。

合成

请注意,CLP(FD) 约束可以自然地与干净的表示一起使用。例如,为了从上面解释的更细粒度的类型错误中受益,您可以编写:

list_first_integer([i(I)|_], I) :-我在 inf..sup.
list_first_integer([o(_)|Ls], I) :-
        list_first_integer(Ls, I)。

现在,你得到:

?- list_first_integer([i(a)], I)。
错误:类型错误:预期为“整数”,找到“a”(原子)

最初,您可能会面临默认的表示。根据我的经验,一个好的方法是尽快将其转换为干净的表示形式,以便在程序的其余部分中以符号方式区分所有情况,以免产生歧义。

于 2017-05-01T20:28:48.220 回答