## 4.20 Analysing and Constructing Terms

[ISO]functor(?Term, ?Functor, ?Arity)
  True if Term is a term with functor Functor and arity Arity. If Term is a variable it is unified with a new term holding only variables. functor/3 silently fails on instantiation faults44In version 1.2 instantiation faults led to error messages. The new version can be used to do type testing without the need to catch illegal instantiations first. If Term is an atom or number, Functor will be unified with Term and arity will be unified with the integer 0 (zero).
[ISO]arg(?Arg, +Term, ?Value)
  Term should be instantiated to a term, Arg to an integer between 1 and the arity of Term. Value is unified with the Arg-th argument of Term. Arg may also be unbound. In this case Value will be unified with the successive arguments of the term. On successful unification, Arg is unified with the argument number. Backtracking yields alternative solutions.45The instantiation pattern (-, +, ?) is an extension to `standard' Prolog. Some systems provide genarg/3 that covers this pattern. The predicate arg/3 fails silently if Arg = 0 or Arg > arity and raises the exception `domain_error(not_less_then_zero, Arg)` if Arg < 0.
[ISO]?Term =.. ?List
  List is a list which head is the functor of Term and the remaining arguments are the arguments of the term. Each of the arguments may be a variable, but not both. This predicate is called `Univ'. Examples:

      ?- foo(hello, X) =.. List.

      List = [foo, hello, X]

      ?- Term =.. [baz, foo(1)]

      Term = baz(foo(1))

numbervars(+Term, +Start, -End)
  Unify the free variables of Term with a term `$VAR(N)`, where N is the number of the variable. Counting starts at Start. End is unified with the number that should be given to the next variable. Example:

      ?- numbervars(foo(A, B, A), 0, End).

      A = '$VAR'(0)
      B = '$VAR'(1)
      End = 2

  See also the `numbervars` option to write_term/3 and numbervars/4.

numbervars(+Term, +Start, -End, +Options)
  As numbervars/3, but providing the following options:
  functor_name(+Atom)
    Name of the functor to use instead of `$VAR`.
  attvar(+Action)
    What to do if an attributed variable is encountered. Options are `skip`, which causes numbervars/3 to ignore the attributed variable, `bind` which causes it to thread it as a normal variable and assign the next `'$VAR'`(N) term to it or (default) `error` which raises the a `type_error` exception.46This behaviour was decided after a long discussion between David Reitter, Richard O'Keefe, Bart Demoen and Tom Schrijvers.
  singletons(+Bool)
    If `true` (default `false`), numbervars/4 does singleton detection. Singleton variables are unified with `'$VAR'('_')`, causing them to be printed as `_` by write_term/2 using the numbervars option. This option is exploited by portray_clause/2 and write_canonical/2.bugCurrently this option is ignored for cyclic terms.
term_variables(+Term, -List)
  Unify List with a list of variables, each sharing with a unique variable of Term.47This predicate used to be called free_variables/2 . The name term_variables/2 is more widely used. The old predicate is still available from the library `library(backcomp)`. The variables in List are ordered in order of appearance traversing Term depth-first and left-to-right. See also term_variables/3. For example:

      ?- term_variables(a(X, b(Y, X), Z), L).

      L = [G367, G366, G371]
      X = G367
      Y = G366
      Z = G371

term_variables(+Term, -List, ?Tail)
  Difference list version of term_variables/2. I.e. Tail is the tail of the variable-list List.
[ISO]copy_term(+In, -Out)
  Create a version if In with renamed (fresh) variables and unify it to Out. Attributed variables (see section 6.1) have their attributed copied. The implementation of copy_term/2 can deal with infinite trees (cyclic terms). As pure Prolog cannot distinguish a ground term from another ground term with exactly the same structure, ground sub-terms are shared between In and Out. Sharing ground terms does affect setarg/3. SWI-Prolog provides duplicate_term/2 to create a true copy of a term.

### 4.20.1 Non-logical operations on terms

Prolog is not capable to modify instantiated parts of a term. Lacking that capability makes that language much safer, but unfortunately there are problems that suffer severely in terms of time and/or memory usage. Always try hard to avoid the use of these primitives, but they can be a good alternative to using dynamic predicates. See also section 6.3, discussing the use of global variables.

setarg(+Arg, +Term, +Value)
  Extra-logical predicate. Assigns the Arg-th argument of the compound term Term with the given Value. The assignment is undone if backtracking brings the state back into a position before the setarg/3 call. See also nb_setarg/3.

  This predicate may be used for destructive assignment to terms, using them as an extra-logical storage bin. Always try hard to avoid the use of setarg/3 as it is not supported by many Prolog systems and one has to be very careful about unexpected copying as well as unexpected not copying of terms.

nb_setarg(+Arg, +Term, +Value)
  Assigns the Arg-th argument of the compound term Term with the given Value as setarg/3, but on backtracking the assignment is not reversed. If Term is not atomic, it is duplicated using duplicate_term/2. This predicate uses the same technique as nb_setval/2. We therefore refer to the description of nb_setval/2 for details on non-backtrackable assignment of terms. This predicate is compatible with GNU-Prolog `setarg(A,T,V,false)`, removing the type-restriction on Value. See also nb_linkarg/3. Below is an example for counting the number of solutions of a goal. Note that this implementation is thread-safe, reentrant and capable of handling exceptions. Realising these features with a traditional implementation based on assert/retract or flag/3 is much more complicated.

      :- meta_predicate
              succeeds_n_times(0, -).

      succeeds_n_times(Goal, Times) :-
              Counter = counter(0),
              (   Goal,
                  arg(1, Counter, N0),
                  N is N0 + 1,
                  nb_setarg(1, Counter, N),
                  fail
              ;   arg(1, Counter, Times)
              ).

nb_linkarg(+Arg, +Term, +Value)
  As nb_setarg/3, but like nb_linkval/2 it does not duplicate Value. Use with extreme care and consult the documentation of nb_linkval/2 before use.
duplicate_term(+In, -Out)
  Version of copy_term/2 that also copies ground terms and therefore ensures destructive modification using setarg/3 does not affect the copy. See also nb_setval/2, nb_linkval/2, nb_setarg/3 and nb_linkarg/3.
[semidet]same_term(@T1, @T2)
  True if T1 and T2 are the equivalent and will remain the equivalent, even if setarg/3 is used on either of them. This means T1 and T2 are the same variable, equivalent atomic data or a compound term allocated at the same address.