Next: Forloop, Previous: Ifelse, Up: Conditionals
m4
There is no direct support for loops in m4
, but macros can be
recursive. There is no limit on the number of recursion levels, other
than those enforced by your hardware and operating system.
Loops can be programmed using recursion and the conditionals described previously.
There is a builtin macro, shift
, which can, among other things,
be used for iterating through the actual arguments to a macro:
Takes any number of arguments, and expands to all its arguments except arg1, separated by commas, with each argument quoted.
The macro
shift
is recognized only with parameters.
shift =>shift shift(`bar') => shift(`foo', `bar', `baz') =>bar,baz
An example of the use of shift
is this macro:
It is implemented as:
define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', `reverse(shift($@)), `$1'')') => reverse => reverse(`foo') =>foo reverse(`foo', `bar', `gnats', `and gnus') =>and gnus, gnats, bar, foo
While not a very interesting macro, it does show how simple loops can be
made with shift
, ifelse
and recursion. It also shows
that shift
is usually used with `$@'. Sometimes, a
recursive algorithm requires adding quotes to each element:
Takes any number of arguments, and adds quoting. With
quote
, only one level of quoting is added, effectively removing whitespace after commas and turning multiple arguments into a single string. Withdquote
, two levels of quoting are added, one around each element, and one around the list. And withdquote_elt
, two levels of quoting are added around each element.
An actual implementation of these three macros is distributed as m4-1.4.8/examples/quote.m4 in this package. First, let's examine their usage:
include(`quote.m4') => -quote-dquote-dquote_elt- =>---- -quote()-dquote()-dquote_elt()- =>--`'-`'- -quote(`1')-dquote(`1')-dquote_elt(`1')- =>-1-`1'-`1'- -quote(`1', `2')-dquote(`1', `2')-dquote_elt(`1', `2')- =>-1,2-`1',`2'-`1',`2'- define(`n', `$#')dnl -n(quote(`1', `2'))-n(dquote(`1', `2'))-n(dquote_elt(`1', `2'))- =>-1-1-2- dquote(dquote_elt(`1', `2')) =>``1'',``2'' dquote_elt(dquote(`1', `2')) =>``1',`2''
The last two lines show that when given two arguments, dquote
results in one string, while dquote_elt
results in two. Now,
examine the implementation. Note that quote
and
dquote_elt
make decisions based on their number of arguments, so
that when called without arguments, they result in nothing instead of a
quoted empty string; this is so that it is possible to distinquish
between no arguments and an empty first argument. dquote
, on the
other hand, results in a string no matter what, since it is still
possible to tell whether it was invoked without arguments based on the
resulting string.
undivert(`quote.m4')dnl =>divert(`-1') =># quote(args) - convert args to single-quoted string =>define(`quote', `ifelse(`$#', `0', `', ``$*'')') =># dquote(args) - convert args to quoted list of quoted strings =>define(`dquote', ``$@'') =># dquote_elt(args) - convert args to list of double-quoted strings =>define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''', => ```$1'',$0(shift($@))')') =>divert`'dnl