我试图在绑定到某个值之前找到一种设置变量类型的方法。遗憾的是,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).
是否仍然可以设置尚未绑定到值的变量的类型?
答案 0 :(得分:3)
在SWI-Prolog中,我使用when/2
来表示类似的情况。我真的不知道这是不是一个好主意,它肯定感觉像是一个黑客,但我想这很好,如果你只想说“这个变量只能变成X”,其中X是integer
,或number
或atom
等等。
所以:
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).
???
答案 1 :(得分:2)
除了Boris所说的,我建议整数的特定情况:考虑使用 CLP(FD)约束来表示变量必须是整数的。为了表达这个非常一般的要求,您可以为所有整数发布必然包含的CLP(FD)约束。
例如:
?- X in inf..sup. X in inf..sup.
从此时起,X
只能将 实例化为整数。其他所有内容都会产生类型错误。
例如:
?- X in inf..sup, X = 3. X = 3. ?- X in inf..sup, X = a. ERROR: Type error: `integer' expected, found `a' (an atom)
声明性地,您始终可以使用静默失败替换类型错误,因为如果此错误,没有可能的其他实例化可以使程序成功就产生了。
因此,如果您更喜欢无声失败而不是此类型错误,则可以使用catch/3
获取
?- X in inf..sup, catch(X = a, error(type_error(integer,_),_), false). false.
CLP(FD)约束是针对整数量身定制的,让您以方便的方式表达对该特定域的进一步要求。
让我们考虑您get_first_int/2
的具体示例。首先,让我们将其重命名为list_first_integer/3
,以便明确每个参数是什么,并且还表明我们完全打算在几个方向上使用 ,而不仅仅是“获取” ,还要测试,理想情况下生成此关系中的列表和整数。
其次,请注意这个谓词相当混乱,因为它不可靠地取决于列表和整数的实例化,这个属性不能用一阶逻辑表示,而是依赖于外部的东西这个逻辑。如果我们接受这一点,那么一个非常直接的方式来做你主要想要的就是把它写成:
list_first_integer(Ls, I) :- once((member(I0, Ls), integer(I0))), I = I0.
只要列表被充分实例化,这种方法就有效,这在您的示例中似乎是隐含的情况,但一般情况下通常不需要 。例如,使用完全实例化的列表,我们得到:
?- list_first_integer([a,b,c], I). false. ?- list_first_integer([a,b,c,4], I). I = 4. ?- list_first_integer([a,b,c,4], 3). false.
相反,如果列表 没有充分实例化,那么我们会遇到以下主要问题:
?- list_first_integer(Ls, I). nontermination
并进一步:
?- list_first_integer([X,Y,Z], I). false.
即使更具体的实例化成功:
?- X = 0, list_first_integer([X,Y,Z], I). X = I, I = 0.
核心问题是你在这里推理 defaulty 术语:仍然是变量的列表元素可以 实例化为整数或< / em>以后的任何其他术语。一个干净的出路是将数据表示设计为符号区分可能的情况。例如,让我们使用包装器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) ; false. ?- X = i(0), list_first_integer([X,Y,Z], I). X = i(0), I = 0 ; false.
如果我们只使用干净的数据表示,其他示例也仍然有用:
?- list_first_integer([o(a),o(b),o(c)], I). false. ?- list_first_integer([o(a),o(b),o(c),i(4)], I). I = 4 ; false. ?- list_first_integer([o(a),o(b),o(c),i(4)], 3). false.
现在最常见的查询允许我们生成解决方案:
?- 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] ; etc.
价格你需要为这种普遍性付出代价的是这些象征性的包装。由于您似乎关心正确性以及代码的一般性,我认为与更容易出错的默认方法相比,这是一个讨价还价。
请注意,CLP(FD)约束可以自然地与干净的表示一起使用。例如,如上所述,要从更精细的类型错误中受益,您可以写:
list_first_integer([i(I)|_], I) :- I in inf..sup. list_first_integer([o(_)|Ls], I) :- list_first_integer(Ls, I).
现在,你得到:
?- list_first_integer([i(a)], I). ERROR: Type error: `integer' expected, found `a' (an atom)
最初,您可能面临默认表示。根据我的经验,一个好的方法是尽快将其转换为干净的表示,为了您的程序的其余部分,您可以在其中以符号方式区分所有情况,使得不存在歧义。