library(changearg)
The predicates in library(changearg)
allow you to construct a new term
that is identical to an old term except that one of its elements
has been replaced or two of its elements have been swapped.
Using these operations, you could use terms as one-dimensional
arrays; however, though the elements of such arrays can be accessed
in O(1) time using arg/3
, changing an element takes O(N)
time, where N is the arity of the term.
See library(logarr)
for a more efficient way of implementing arrays in Prolog.
Why then are these operations provided? To aid in the construction of term-rewriting systems. For example, suppose you have a set of rewrite rules expressed as a table
rewrite_rule(X*0, 0). rewrite_rule(X*1, X). rewrite_rule(K*X, X*K) :- integer(K). rewrite_rule(X*(Y*Z), (X*Y)*Z). . . .
which you want exhaustively applied to a term. You could write
waterfall(Expr, Final) :- path_arg(Path, Expr, Lhs), rewrite_rule(Lhs, Rhs), change_path_arg(Path, Expr, Modified, Rhs), !, waterfall(Modified, Final). waterfall(Expr, Expr).
Then
| ?- waterfall((a*b)*(c*0)*d, X). X = 0 | ?- waterfall((1*a)*(2*b), X). X = a*2*b
The predicates supplied by library(changearg)
are as follows:
change_arg(
+Index,
?OldTerm,
?OldArg,
?NewTerm,
?NewArg)
change_arg/5
is actually quite symmetric:
change_arg(K, O, X, N, Y)
and
change_arg(K, N, Y, O, X)
have exactly the same effect. For example:
| ?- change_arg(1, c(o,l,t), X, N, u). X = o, N = c(u,l,t) | ?- change_arg(1, N, u, c(o,l,t), X). N = c(u,l,t), X = o
| ?- change_arg(3, SALE, E, s(a,l,t), T). SALE = s(a,l,E), E = _755, T = t | ?- change_arg(3, a+b, b, X, c). no
change_arg(
+Index,
?OldTerm,
?NewTerm,
?NewArg)
change_arg/5
except that the OldArg
argument is omitted. Please note: this argument order may be
surprising if you think about this predicate on its own; however, it
makes sense in the context of the entire group.
change_arg0/[4,5]
change_arg/[4,5]
except that Index=0 is allowed, in
which case the principal function symbol is changed. Do not use
this in new programs; use change_arg/5
or change_functor/5
directly.
The order in which values for Index are enumerated is not defined.
change_functor(
?OldTerm,
?OldSymbol,
?NewTerm,
?NewSymbol,
?Arity)
same_functor/3
in some respects
(lib-tma-samefunctor), such as
the fact that any of the arguments can be solved for. If OldTerm and
NewSymbol are instantiated, or NewTerm and OldSymbol are instantiated,
or NewSymbol, OldSymbol, and Arity are instantiated, that is enough
information to proceed. Note that OldSymbol or NewSymbol may be a
number, in which case Arity must be 0.
swap_args(
+Index1,
+Index2,
?OldTerm,
?Arg1,
?NewTerm,
?Arg2)
at Index1 at Index2 in OldTerm Arg1 Arg2 in NewTerm Arg2 Arg1
that is, the arguments at Index1 and Index2 have been swapped.
As with change_arg/5
, swap_args/6
is symmetric; the following terms
have exactly the same effect.
swap_args(I, J, O, X, N, Y) swap_args(I, J, N, Y, O, X)
For example:
| ?- swap_args(1, 4, f(X,e,a,Y,e,r), r, T, d). X = r, Y = d, T = f(d,e,a,r,e,r)
swap_args(
+Index1,
+Index2,
?OldTerm,
?NewTerm)
swap_args/6
except that the Arg1
and Arg2 arguments are omitted.
| ?- swap_args(1, 4, f(r,e,a,d), X). X = f(d,e,a,r)
change_path_arg(
+Path,
?OldTerm,
?OldSub,
?NewTerm,
?NewSub)
path_arg(Path, OldTerm, OldSub), path_arg(Path, NewTerm, NewSub)
That is, the subterm of OldTerm at Path was OldSub
and is replaced by NewSub in NewTerm, and there are no
other differences between OldTerm and NewTerm.
This is to change_arg/5
as path_arg/3
is to arg/3
.
change_path_arg(
+Path,
?OldTerm,
?NewTerm,
?NewSub)
change_path_arg/5
except that the OldSub
argument is omitted.
| ?- OldTerm = this*is+an*example, | path_arg(Path, OldTerm, this), | change_path_arg(Path, OldTerm, NewTerm, it). OldTerm = this*is+an*example, Path = [1,1], NewTerm = it*is+an*example