Finding a Term's Arguments -- library(arg)

library(arg) defines seven predicates, all of which are generalizations of the built-in predicate arg/3.


arg(+ArgNum, +Term, -Arg)
unifies Arg with the ArgNumth argument of Term. Term must not be a variable, but any other kind of term is acceptable. Even a number is acceptable as Term; numbers are simply terms that happen to have no arguments. ArgNum must be instantiated to an integer. If ArgNum is less than 1 or greater than the number of arguments of Term, arg/3 signals an error. Basically, arg/3 pretends to be the infinite table
          arg(1, a(X), X).
          arg(1, a(X,_), X).
          arg(2, a(_,X), X).
          ...
          arg(5, zebra_finch(_,_,_,_,X,_,_,_), X).
          ...
          

except that it can only be used to find the Arg for a given Index and Term, and cannot find the Index. arg/3 is a built-in predicate, and is described in the reference pages, not actually defined in library(arg).

arg0(+Index, +Term, ?Arg)
unifies Arg with the Indexth argument of Term if Index > 0, or with the principal function symbol of Term if Index = 0. This predicate is supplied because some other Prolog implementations have made arg/3 do this, and this makes it easier to convert code originally written for those systems. The one reason you might use arg0/3 is that it reports errors, while arg/3, for backwards compatibility with DEC-10 Prolog, does not. Examples:
          | ?- arg0(2, f(o,x,y), X).
          
          X = x
          
          | ?- arg0(0, f(o,x,y), X).
          
          X = f
          
          | ?- arg0(N, f(o,x,y), X).
          ! Instantiation error in argument 1 of arg0/3
          ! goal:  arg0(_732,f(o,x,y),_767)
          
          | ?- arg0(y, f(o,x,y), N).
          ! Type error in argument 1 of arg0/3
          ! integer expected, but y found
          ! goal:  arg0(y,f(o,x,y),_764)
          

genarg(?Index, +Term, ?Arg)
is a version of arg/3 that is able to solve for Index as well as for Arg.
          | ?- arg(N, f(a,b), X).
          
          no
          
          | ?- genarg(N, f(a,b), X).
          
          N = 2,
          X = b ;
          
          N = 1,
          X = a ;
          
          no
          
          | ?- genarg(N, f(1,b,2), X), atom(X).
          
          N = 2,
          X = b ;
          
          no
          
          | ?- genarg(3, f(1,b,2), X).
          
          X = 2
          

If Index is instantiated, genarg/3 generates the same result as arg/3. If Index is uninstantiated, genarg/3 picks out each argument in turn. The order in which the arguments are tried is not defined; the current implementation works from right to left, but this order should not be relied upon.

genarg0(?Index, +Term, ?Arg)
is a version of arg0/3 that is able to solve for Index as well as Arg.
args(?Index, +Terms, ?Args)
is true when Terms and Args are lists of the same length, each element of Terms is instantiated to a term having at least Index arguments, and arg(Index, Term, Arg) is true for each pair <Term, Arg> of corresponding elements of <Terms, Args>. Index is strictly positive, and only arguments are found, not principal function symbols. This is a generalization of genarg/3. For example,
          | ?- args(1, [a+b,c-d,e*f,g/h], X).
          
          X = [a,c,e,g]
          
          | ?- args(2, [a+A,c-B,e*C,g/D], [b,d,f,h]).
          
          A = b,
          B = d,
          C = f,
          D = h
          
          | ?- args(I, [1-a,2-b,3-c,4-d], X).
          
          I = 2,
          X = [a,b,c,d] ;
          
          I = 1,
          X = [1,2,3,4]
          

args0(?Index, +Terms, ?Args)
is like args/3 except that Index = 0 selects the principal function symbol.
          | ?- args0(0, [a+b,c-d,e*f,g/h,27], X).
          
          X = [+,-,*,/,27]
          
          | ?- args0(I, [1-a,2-b,3-c,4-d], X).
          
          I = 2,
          X = [a,b,c,d] ;
          
          I = 1,
          X = [1,2,3,4] ;
          
          I = 0,
          X = [-,-,-,-]
          

This is a generalization of genarg0/3.

project(+Terms, ?Index, ?Args)
is identical to args0/3 except for the argument order. The argument order of project/3 is not consistent with anything else in the library. This predicate is retained for backwards compatibility. Use args0/3 instead in new programs.
path_arg(?Path, +Term, ?SubTerm)
unifies SubTerm with the subterm of Term found by following Path, where Path is a sequence of positive integers. For example, the goal
          path_arg([I,J], MyTerm, MySubTerm)
          

unifies MySubTerm with the J'th argument of the I'th argument of MyTerm. In general, Term should be ground. path_arg/3 may be regarded as a generalization of genarg/3. It can be used to find the SubTerm and a known Path, or to find a Path to a known SubTerm. It could have been defined as

          path_arg([], Term, Term).
          path_arg([Index|Indices], Term, SubTerm) :-
                  genarg(Index, Term, Arg),
                  path_arg(Indices, Arg, SubTerm).
          

The actual library program is rather more complicated because it contains error-reporting code. Examples of its use include:

          /* Here is a sample table of all the subterms of
          /* the quadratic formula "(a*x^2) + (b*x) + c = 0"
          /*
          []          a*x^2+b*x+c=0
          [1]         a*x^2+b*x+c
          [1,1]       a*x^2+b*x
          [1,1,1]     a*x^2
          [1,1,1,1]   a
          [1,1,1,2]     x^2
          [1,1,1,2,1]   x
          [1,1,1,2,2]     2
          [1,1,2]           b*x
          [1,1,2,1]         b
          [1,1,2,2]           x
          [1,2]                 c
          [2]                     0
          */
          
          | ?- path_arg([1,1,2,2],   a*x^2+b*x+c=0, X).
          
          X = x                              ^
          
          | ?- path_arg([1,1,1,2,2], a*x^2+b*x+c=0, X).
          
          X = 2                          ^
          
          | ?- path_arg(Path,        a*x^2+b*x+c=0, b).
          
          Path = [1,1,2,1]                 ^
          

This notation for locating subtrees of a tree is widely used throughout computer science.

Note that except for project/3, which is included only in the interests of backwards compatibility, all of these predicates have the same pattern of arguments:

For consistency, we recommend that you use this argument order for "selector" predicates generally: first the argument or arguments that constitute the selector or index, then the thing or things that are being selected from, and finally the result or results.