整数的特定约束
除了鲍里斯所说的,我对整数的特殊情况有一个建议:考虑使用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”(原子)
最初,您可能会面临默认的表示。根据我的经验,一个好的方法是尽快将其转换为干净的表示形式,以便在程序的其余部分中以符号方式区分所有情况,以免产生歧义。