测试 Prolog 术语的类型:小问题

Testing types of Prolog terms: slight problems

这里是一些测试 SWI-Prolog 术语 type/metatype 的代码:

% var,nonvar are meta-question about the state of computation, not about the term
% Note: X can be a variable (if it is fresh) or a nonvar (if it is set)
%       but x can ever only be a nonvar

typeof(X, var)           :- var(X),!.     % if it's a variable, we don't look for any other match
typeof(X, nonvar)        :- nonvar(X).    % nonvar(X) is the complement of var(X)

typeof(X, int)           :- integer(X).   % is there a way to distinguish "machine ints" from "bigints"? 
typeof(X, float)         :- float(X).     % IEEE 754 floats; magnitude: always double (what if long doublr arrives?)
typeof(X, number)        :- number(X).    % rationals are not (yet) numbers
typeof(X, rational)      :- rational(X).  % covers "big decimals" too

typeof(X, atom)          :- atom(X).
typeof(X, atomic)        :- atomic(X).

typeof(X, string)        :- string(X).

typeof(X, blob(T))       :- blob(X,T).

typeof(X, compound(F,A)) :- compound(X), X =.. [F|Args], length(Args,A).

typeof(X, ground)        :- ground(X).

typeof(X, cyclic)        :- cyclic_term(X).

typeof(X, list(Len))     :- is_list(X),length(X,Len).    % being a "list" is specific nonlocal phenomenon

typeof(X, conj(Len))     :- nonvar(X),is_conjunction(X,Len).
typeof(X, disj(Len))     :- nonvar(X),is_disjunction(X,Len).

typeof((_A->_B) , impl).
typeof(:-_A     , directive).
typeof(_H:-_B   , clause).

is_conjunction((_,B),Len) :- is_conjunction(B,Lb),!,Len is Lb+1.
is_conjunction((_,_),1).

is_disjunction((_;B),Len) :- is_disjunction(B,Lb),!,Len is Lb+1.
is_disjunction((_;_),1).

%
% TEST CODE
%

:- begin_tests(type_inspection).

test('empty list')      :- is_this_right([]               , [atomic,ground,nonvar,blob(reserved_symbol),list(0)]).
test('an atom')         :- is_this_right(foo_the_atom     , [atom,atomic,ground,nonvar,blob(text)]).
test('a string')        :- is_this_right("foo_the_string" , [atomic,ground,nonvar,string]).
test('a variable')      :- is_this_right(_VAR             , [var]).
test('integer')         :- is_this_right(4377                 , [atomic,ground,int,nonvar,number,rational]).
test('big integer')     :- is_this_right(43759624750933342417 , [atomic,ground,int,nonvar,number,rational]).
test('decimal')         :- is_this_right(5600.556 , [atomic,float,ground,nonvar,number]).
test('float')           :- A is 2/6, is_this_right(A, [atomic,float,ground,nonvar,number]).
test('/ compound')      :- is_this_right(2/6, [ground,nonvar,compound('/',2)]).
test('float')           :- is_this_right(1.234    , [atomic,float,ground,nonvar,number]).
test('rational')        :- is_this_right(5r6      , [atomic,ground,nonvar,number,rational]).
test('rational')        :- R is 5 rdiv 6, is_this_right(R, [atomic,ground,nonvar,number,rational]).
test('cyclic compound') :- A = a(1,2,3,A), is_this_right(A, [cyclic,ground,nonvar,compound(a,4)]).

test('dict')            :- is_this_right(_{a:1,b:2}, [nonvar,compound(dict,5)]).
test('cyclic list')     :- A = [1,2,3|A], is_this_right(A, [cyclic,ground,nonvar,compound(['|'],2)]).
test('nonempty list')   :- is_this_right([1,2,3]          , [ground,nonvar,list(3),compound(['|'],2)]).

setify(A,B,As,Bs) :-      % A,B -> As,Bs
   list_to_ord_set(A,As), % A->As
   list_to_ord_set(B,Bs). % B->Bs

is_this_right(Value,Expected) :-
   setof(T,typeof(Value,T),Ts),
   verdict(Value,Expected,Ts).

verdict(Value,Expected,Got) :-   % V,E,G -> b?
   setify(Expected,Got,Eos,Gos),  
   (ord_seteq(Eos,Gos)
    -> format("For ~w we have: ~w\n",[Value,Gos])
    ;  (format("For ~w, expected ~w but got ~w\n",[Value,Eos,Gos]),fail)).

:- end_tests(type_inspection).


rt :- run_tests(type_inspection).

如果我们 运行 目标 rt,我们得到这个(格式不正确):

这些测试通过:

For [] we have: [atomic,ground,nonvar,blob(reserved_symbol),list(0)]
For foo_the_atom we have: [atom,atomic,ground,nonvar,blob(text)]
For foo_the_string we have: [atomic,ground,nonvar,string]
For _15852 we have: [var]
For 4377 we have: [atomic,ground,int,nonvar,number,rational]
For 43759624750933342417 we have: [atomic,ground,int,nonvar,number,rational]
For 5600.556 we have: [atomic,float,ground,nonvar,number]
For 0.3333333333333333 we have: [atomic,float,ground,nonvar,number]
For 2/6 we have: [ground,nonvar,compound(/,2)]
For 1.234 we have: [atomic,float,ground,nonvar,number]
For 5r6 we have: [atomic,ground,nonvar,number,rational]
For 5r6 we have: [atomic,ground,nonvar,number,rational]
For @(S_1,[S_1=a(1,2,3,S_1)]) we have: [cyclic,ground,nonvar,compound(a,4)]

但是 | 函子的复合项有问题。我必须使用什么符号才能使测试通过?

For _15714{a:1,b:2}, 
expected [nonvar,compound(dict,5)] 
but got [nonvar,compound(dict,5)]

For @(S_1,[S_1=[1,2,3|S_1]]), 
expected [cyclic,ground,nonvar,compound([|],2)] 
but got [cyclic,ground,nonvar,compound([|],2)]

For [1,2,3], expected [ground,nonvar,list(3),compound([|],2)]
but got [ground,nonvar,list(3),compound([|],2)]

此外,有没有办法区分"big integers"和"machine integers"(除了检查星等?)

您应该使用 ~q 来写术语(即写引号),而不是 ~w。尝试:

verdict(Value,Expected,Got) :-   % V,E,G -> b?
   setify(Expected,Got,Eos,Gos),  
   (   ord_seteq(Eos,Gos)
   ->  format("For ~q we have: ~q\n",[Value,Gos])
   ;   format("For ~q, expected ~q but got ~q\n",[Value,Eos,Gos]),
       fail
   ).

否则您可能会误导结果的解释:

For _4414{a:1,b:2},
    expected [nonvar,compound(dict,5)]
    but got [nonvar,compound(C'dict',5)]
ERROR: user://1:68:
    test dict: failed

For @(S_1,[S_1=[1,2,3|S_1]]),
   expected [cyclic,ground,nonvar,compound(['|'],2)]
   but got [cyclic,ground,nonvar,compound('[|]',2)]
ERROR: user://1:69:
    test cyclic list: failed

For [1,2,3],
    expected [ground,nonvar,list(3),compound(['|'],2)]
    but got [ground,nonvar,list(3),compound('[|]',2)]
ERROR: user://1:70:
    test nonempty list: failed

注意:

?- functor([1,2,3], Functor, Arity).
Functor = '[|]',
Arity = 2.

?- functor(key{a:1,b:2}, Functor, Arity).
Functor = C'dict',
Arity = 5.

为什么? SWI-Prolog 更改了传统和标准的 '.'/2 列表仿函数,以便它可以将其用于字典。由于解析方式的不同,Dicts 的仿函数看起来很奇怪。