1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2008-2025, University of Amsterdam, 7 VU University 8 SWI-Prolog Solutions b.v. 9 Amsterdam All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(terms, 38 [ term_hash/2, % @Term, -HashKey 39 term_hash/4, % @Term, +Depth, +Range, -HashKey 40 term_size/2, % @Term, -Size 41 term_variables/2, % @Term, -Variables 42 term_variables/3, % @Term, -Variables, +Tail 43 variant/2, % @Term1, @Term2 44 subsumes/2, % +Generic, @Specific 45 subsumes_chk/2, % +Generic, @Specific 46 cyclic_term/1, % @Term 47 acyclic_term/1, % @Term 48 term_subsumer/3, % +Special1, +Special2, -General 49 term_factorized/3, % +Term, -Skeleton, -Subsitution 50 mapargs/3, % :Goal, ?Term1, ?Term2 51 mapsubterms/3, % :Goal, ?Term1, ?Term2 52 mapsubterms_var/3, % :Goal, ?Term1, ?Term2 53 foldsubterms/4, % :Goal, +Term, +State0, -State 54 foldsubterms/5, % :Goal, +Term1, ?Term2, +State0, -State 55 same_functor/2, % ?Term1, ?Term2 56 same_functor/3, % ?Term1, ?Term2, -Arity 57 same_functor/4 % ?Term1, ?Term2, ?Name, ?Arity 58 ]). 59 60:- meta_predicate 61 mapargs(,,), 62 mapsubterms(,,), 63 mapsubterms_var(,,), 64 foldsubterms(,,,), 65 foldsubterms(,,,,). 66 67:- autoload(library(rbtrees), 68 [ rb_empty/1, 69 rb_lookup/3, 70 rb_insert/4, 71 rb_new/1, 72 rb_visit/2, 73 ord_list_to_rbtree/2, 74 rb_update/5 75 ]). 76:- autoload(library(error), [instantiation_error/1]).
?- A = a(1,2,3), term_size(A,S). S = 4. ?- A = a(1,2,3), term_size(a(A,A),S). S = 7. ?- term_size(a(a(1,2,3), a(1,2,3)), S). S = 11.
Note that small objects such as atoms and small integers have a size 0. Space is allocated for floats, large integers, strings and compound terms.
108term_size(Term, Size) :-
109 '$term_size'(Term, _, Size).Term1 =@= Term2.
115variant(X, Y) :-
116 X =@= Y.
125subsumes_chk(Generic, Specific) :-
126 subsumes_term(Generic, Specific).
138subsumes(Generic, Specific) :-
139 subsumes_term(Generic, Specific),
140 Generic = Specific.151% It has been rewritten by Jan Wielemaker to use the YAP-based 152% red-black-trees as mapping rather than flat lists and use arg/3 153% to map compound terms rather than univ and lists. 154 155term_subsumer(S1, S2, G) :- 156 cyclic_term(S1), 157 cyclic_term(S2), 158 !, 159 rb_empty(Map), 160 lgg_safe(S1, S2, G, Map, _). 161term_subsumer(S1, S2, G) :- 162 rb_empty(Map), 163 lgg(S1, S2, G, Map, _). 164 165lgg(S1, S2, G, Map0, Map) :- 166 ( S1 == S2 167 -> G = S1, 168 Map = Map0 169 ; compound(S1), 170 compound(S2), 171 functor(S1, Name, Arity), 172 functor(S2, Name, Arity) 173 -> functor(G, Name, Arity), 174 lgg(0, Arity, S1, S2, G, Map0, Map) 175 ; rb_lookup(S1+S2, G0, Map0) 176 -> G = G0, 177 Map = Map0 178 ; rb_insert(Map0, S1+S2, G, Map) 179 ). 180 181lgg(Arity, Arity, _, _, _, Map, Map) :- !. 182lgg(I0, Arity, S1, S2, G, Map0, Map) :- 183 I is I0 + 1, 184 arg(I, S1, Sa1), 185 arg(I, S2, Sa2), 186 arg(I, G, Ga), 187 lgg(Sa1, Sa2, Ga, Map0, Map1), 188 lgg(I, Arity, S1, S2, G, Map1, Map).
197lgg_safe(S1, S2, G, Map0, Map) :- 198 ( S1 == S2 199 -> G = S1, 200 Map = Map0 201 ; rb_lookup(S1+S2, G0, Map0) 202 -> G = G0, 203 Map = Map0 204 ; compound(S1), 205 compound(S2), 206 functor(S1, Name, Arity), 207 functor(S2, Name, Arity) 208 -> functor(G, Name, Arity), 209 rb_insert(Map0, S1+S2, G, Map1), 210 lgg_safe(0, Arity, S1, S2, G, Map1, Map) 211 ; rb_insert(Map0, S1+S2, G, Map) 212 ). 213 214lgg_safe(Arity, Arity, _, _, _, Map, Map) :- !. 215lgg_safe(I0, Arity, S1, S2, G, Map0, Map) :- 216 I is I0 + 1, 217 arg(I, S1, Sa1), 218 arg(I, S2, Sa2), 219 arg(I, G, Ga), 220 lgg_safe(Sa1, Sa2, Ga, Map0, Map1), 221 lgg_safe(I, Arity, S1, S2, G, Map1, Map).
?- X = a(X), term_factorized(b(X,X), Y, S). Y = b(_G255, _G255), S = [_G255=a(_G255)].
238term_factorized(Term, Skeleton, Substitutions) :- 239 rb_new(Map0), 240 add_map(Term, Map0, Map), 241 rb_visit(Map, Counts), 242 common_terms(Counts, Common), 243 ( Common == [] 244 -> Skeleton = Term, 245 Substitutions = [] 246 ; ord_list_to_rbtree(Common, SubstAssoc), 247 insert_vars(Term, Skeleton, SubstAssoc), 248 mk_subst(Common, Substitutions, SubstAssoc) 249 ). 250 251add_map(Term, Map0, Map) :- 252 ( primitive(Term) 253 -> Map = Map0 254 ; rb_update(Map0, Term, Old, New, Map) 255 -> New is Old+1 256 ; rb_insert(Map0, Term, 1, Map1), 257 assoc_arg_map(1, Term, Map1, Map) 258 ). 259 260assoc_arg_map(I, Term, Map0, Map) :- 261 arg(I, Term, Arg), 262 !, 263 add_map(Arg, Map0, Map1), 264 I2 is I + 1, 265 assoc_arg_map(I2, Term, Map1, Map). 266assoc_arg_map(_, _, Map, Map). 267 268primitive(Term) :- 269 var(Term), 270 !. 271primitive(Term) :- 272 atomic(Term), 273 !. 274primitive('$VAR'(_)). 275 276common_terms([], []). 277common_terms([H-Count|T], List) :- 278 !, 279 ( Count == 1 280 -> common_terms(T, List) 281 ; List = [H-_NewVar|Tail], 282 common_terms(T, Tail) 283 ). 284 285insert_vars(T0, T, _) :- 286 primitive(T0), 287 !, 288 T = T0. 289insert_vars(T0, T, Subst) :- 290 rb_lookup(T0, S, Subst), 291 !, 292 T = S. 293insert_vars(T0, T, Subst) :- 294 functor(T0, Name, Arity), 295 functor(T, Name, Arity), 296 insert_arg_vars(1, T0, T, Subst). 297 298insert_arg_vars(I, T0, T, Subst) :- 299 arg(I, T0, A0), 300 !, 301 arg(I, T, A), 302 insert_vars(A0, A, Subst), 303 I2 is I + 1, 304 insert_arg_vars(I2, T0, T, Subst). 305insert_arg_vars(_, _, _, _). 306 307mk_subst([], [], _). 308mk_subst([Val0-Var|T0], [Var=Val|T], Subst) :- 309 functor(Val0, Name, Arity), 310 functor(Val, Name, Arity), 311 insert_arg_vars(1, Val0, Val, Subst), 312 mk_subst(T0, T, Subst).
call(Goal, A1, A2) is true.320mapargs(Goal, Term1, Term2) :- 321 same_functor(Term1, Term2, Arity), 322 mapargs_(1, Arity, Goal, Term1, Term2). 323 324mapargs_(I, Arity, Goal, Term1, Term2) :- 325 I =< Arity, 326 !, 327 arg(I, Term1, A1), 328 arg(I, Term2, A2), 329 call(Goal, A1, A2), 330 I2 is I+1, 331 mapargs_(I2, Arity, Goal, Term1, Term2). 332mapargs_(_, _, _, _, _).
call(Goal, ST1, ST2) succeeds. Procedurably, the
mapping for each (sub) term pair T1/T2 is defined as:
call(Goal, T1, T2) succeeds we are done. Note that the
mapping does not continue in T2. If this is desired, Goal
must call mapsubterms/3 explicitly as part of its conversion.Both predicates are implemented using foldsubterms/5.
358mapsubterms(Goal, Term1, Term2) :- 359 foldsubterms(map2(Goal), Term1, Term2, _, _). 360mapsubterms_var(Goal, Term1, Term2) :- 361 foldsubterms(map2_var(Goal), Term1, Term2, _, _). 362 363map2(Goal, Term1, Term2, _, _) :- 364 nonvar(Term1), 365 call(Goal, Term1, Term2). 366 367map2_var(Goal, Term1, Term2, _, _) :- 368 call(Goal, Term1, Term2).
call(Goal4, SubTerm1, SubTerm2,
StateIn, StateOut) for each subterm, including variables, in Term1.
If this call fails, StateIn and StateOut are the same. This
predicate may be used to map subterms in a term while collecting
state about the mapped subterms. The foldsubterms/4 variant does not
map the term.380foldsubterms(Goal, Term1, State0, State) :- 381 foldsubterms(fold1(Goal), Term1, _, State0, State). 382 383fold1(Goal, Term1, _Term2, State0, State) :- 384 call(Goal, Term1, State0, State). 385 386foldsubterms(Goal, Term1, Term2, State0, State) :- 387 call(Goal, Term1, Term2, State0, State), 388 !. 389foldsubterms(Goal, Term1, Term2, State0, State) :- 390 is_dict(Term1), 391 !, 392 dict_pairs(Term1, Tag, Pairs1), 393 fold_dict_pairs(Pairs1, Pairs2, Goal, State0, State), 394 dict_pairs(Term2, Tag, Pairs2). 395foldsubterms(Goal, Term1, Term2, State0, State) :- 396 is_list(Term1), 397 !, 398 fold_some(Term1, Term2, Goal, State0, State). 399foldsubterms(Goal, Term1, Term2, State0, State) :- 400 compound(Term1), 401 !, 402 same_functor(Term1, Term2, Arity), 403 foldsubterms_(1, Arity, Goal, Term1, Term2, State0, State). 404foldsubterms(_, Term, Term, State, State). 405 406fold_dict_pairs([], [], _, State, State). 407fold_dict_pairs([K-V0|T0], [K-V|T], Goal, State0, State) :- 408 foldsubterms(Goal, V0, V, State0, State1), 409 fold_dict_pairs(T0, T, Goal, State1, State). 410 411fold_some([], [], _, State, State). 412fold_some([H0|T0], [H|T], Goal, State0, State) :- 413 foldsubterms(Goal, H0, H, State0, State1), 414 fold_some(T0, T, Goal, State1, State). 415 416foldsubterms_(I, Arity, Goal, Term1, Term2, State0, State) :- 417 I =< Arity, 418 !, 419 arg(I, Term1, A1), 420 arg(I, Term2, A2), 421 foldsubterms(Goal, A1, A2, State0, State1), 422 I2 is I+1, 423 foldsubterms_(I2, Arity, Goal, Term1, Term2, State1, State). 424foldsubterms_(_, _, _, _, _, State, State).
If Arity is 0, Term1 and Term2 are unified with Name for compatibility.
441same_functor(Term1, Term2) :- 442 same_functor(Term1, Term2, _Name, _Arity). 443 444same_functor(Term1, Term2, Arity) :- 445 same_functor(Term1, Term2, _Name, Arity). 446 447same_functor(Term1, Term2, Name, Arity) :- 448 ( nonvar(Term1) 449 -> functor(Term1, Name, Arity, Type), 450 functor(Term2, Name, Arity, Type) 451 ; nonvar(Term2) 452 -> functor(Term2, Name, Arity, Type), 453 functor(Term1, Name, Arity, Type) 454 ; functor(Term2, Name, Arity), 455 functor(Term1, Name, Arity) 456 )
Term manipulation
Compatibility library for term manipulation predicates. Most predicates in this library are provided as SWI-Prolog built-ins.