Inspecting and managing terms
Type checks, comparison, copy_term, terms as strings, univ, functor, arg, and console output.
Library predicates to deal with terms
So far unification is our only means to manage terms: it has been used to inspect terms (to process "input") and to create new terms (to produce "output"). Often additional expressiveness is needed, though somewhat extra-relational, like the cut.
The usual predicates and operators:
| purpose | predicates |
|---|---|
| inspect the shape of a term | var/1, nonvar/1, number/1, float/1, integer/1, atom/1, compound/1, ground/1 |
| comparison | =/2, \=/2, ==/2, \==/2, copy_term/2 |
| terms as strings | atom_chars/2, atom_codes/2, atom_concat/3, char_code/2, number_chars/2, number_codes/2 |
| term structure | =../2, functor/3, arg/3 |
| I/O over file and console | write/1, nl/0, ... |
Inspecting terms
File inspecting-terms.pl. Typically used to check the correctness of inputs.
?- atom(a)?- atom(10)?- atom(a(1))?- var(X)?- var(a)?- nonvar(X)?- number(10)?- float(10.1)?- integer(10.1)?- compound(10)?- compound(a)?- compound(a(10, 20))?- ground(a(10, 20))?- ground(a(10, X))Term comparison
File term-comparison.pl. Typically used to handle and compare inputs more flexibly.
=/2unifies;\=/2is the non-unification test and never binds;==/2is equality (no binding): two terms are equal if they are structurally identical, so a variable is equal only to itself;\==/2is the inequality;copy_term/2clones a term, giving the copy fresh variables.
?- a(10, b) = a(10, b)?- a(X, b) = a(10, b)?- a(X, b) = a(Y, c)?- a(10, b) \= a(10, b)?- a(X, b) \= a(10, b)?- a(X, b) \= a(Y, c)?- a(10, b) == a(10, b)?- a(X, b) == a(10, b)?- a(X, b) == a(X, b)?- a(10, b) \== a(10, b)?- a(X, b) \== a(10, b)?- a(X, b) \== a(X, b)?- copy_term(a(10, X), Y)?- copy_term(a(10, X), a(10, Y))?- copy_term(a(10, X), a(10, X))?- copy_term(a(10, X), a(11, X))Notice that copy_term(a(10, X), Y) answers Y = a(10, _): the copy has a fresh variable, not X itself. Check it with ==:
?- copy_term(a(10, X), Y), Y == a(10, X)Terms as strings
File terms-as-strings.pl. Typically used to manipulate numbers, atoms and strings computationally.
?- atom_chars(hello, L)?- atom_chars(X, [h, e, l, l, o])?- atom_codes(hello, L)?- atom_codes(X, [95, 48, 32, 49])?- atom('_0 1')?- atom_concat(aa, bb, X)?- number_chars(100, X)?- number_codes(100, X)?- char_code(C, 0'a)The pair atom_chars / atom_codes converts an atom to and from a sequence of one-character atoms or of ASCII codes. Note that atom_codes(X, [95, 48, 32, 49]) gives the atom '_0 1': any text can be an atom.
Compound terms: (de)structuring
File term-structure.pl. Typically used to manipulate compound terms computationally.
?- p(10, q(20)) =.. L?- X =.. [p, 10, q(20)]?- functor(p(10, 20, 30), X, Y)?- functor(T, p, 3)?- arg(2, p(10, 20, 30), Y)=..("univ") converts a compound term to a list (functor first, then the arguments) and back;functor/3extracts, or constructs, the functor name and the arity;arg/3gives the N-th argument of a compound term, directly.
I/O over the console
File console-io.pl. We can redirect standard input/output (it is the console by default), and there are predicates to write terms as strings (write/1, nl/0) and to read. We only see the usage for debugging by logging strings: the program below prints the partial sums while the recursion unwinds.
sum([], 0).
sum([H|T], N) :- sum(T, N2), write(N2), nl, N is H + N2.?- write('start'), nl, sum([10, 20, 30], N)The console shows start, then 0, 30, 50: the sums of the tails, computed from the last element backwards. Here the printed text appears above the answers.
A few examples
File few-examples.pl:
all(+Term, +List): are all the elements ofListof the kindTerm? It usescopy_term/2to compare an element with a fresh copy of the pattern;- a fully relational
size/2, which counts the length of a list when the list is given, and generates a list of the given length when it is not.
% all(+Term, +List): are all elements in List of kind Term?
all(_, []).
all(X, [Y | T]) :- copy_term(X, Y), all(X, T).
% fully-relational size
size(L, N) :- var(L), !, generate(L, N).
size(L, N) :- length(L, N).
generate([], 0) :- !.
generate([_|T], N) :- N2 is N-1, generate(T, N2).?- all(p(X), [p(a), p(b), p(a)])?- all(p(X), [p(a), q(b)])?- size([10, 20, 30], N)?- size(L, 3)all works because copy_term(p(X), Y) gives a fresh p(_), which then unifies with the element, whatever its argument: p(a), p(b) and so on, while q(b) does not match the kind.
Exercise: What kind of term?
Write kind(T, K) where K is one of variable, number, atom or compound, according to the term T. Use the type-checking predicates (watch the order: a variable must be recognised before anything else, and kind must give exactly one answer).