5

I was wondering how to change #1, based on its value, to something else such that any #1 that follows the change would contain the modified value, not the original value (passed to the macro from outside).

\def\myT#1%
  {%
    \ifnum#1=0
      % #1=1
    \fi
    #1
  }
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2

EDIT (work-around; would rather change #1's value directly, like #1=1, than expanding a nested \myT with argument of 1, as below):

\def\myT#1%
  {%
    \ifnum#1=0
      \myT{1}%
    \else
      #1%
    \fi
  }
\myT{0} % 1
\myT{1} % 1
\myT{2} % 2
  • 1
    What about \def\myT#1{\ifnum#1=0 1\else #1\fi}? – Werner Aug 8 at 0:46
  • 4
    You cannot redefine the values of macro parameters. TeX immediately replaces each occurence of a macro parameter with its corresponding value when it expands the macro. At the time you are using the parameters in the replacement text, the parameter tokens are already resolved. What you could do is to store the value of a parameter in a local macro, like \def\1{#1}, and then use and redefine \1 instead. Note that this might lead to semantical changes, e.g. \if#1\foo vs. \if\1\foo. – siracusa Aug 8 at 3:11
  • 1
    Another thing to be aware of is that #1 and friends are not expanded, they are replaced with the corresponding argument when the macro they belong to is expanded (in your case, when a \myT token is expanded). – frougon Aug 8 at 7:51
5

You cannot “assign” values to #1 other than using the appropriate argument.

The type of task you present is more conveniently done with two macros; while you can call \myT{1} when the original argument is 0, this is inefficient as it performs again a check that has already been done.

\def\myT#1{%
  \ifnum#1=0
    \myTaux{1}%
  \else
    \myTaux{#1}%
  \fi
}
\def\myTaux#1{-#1-\par}

\myT{0} % 1
\myT{1} % 1
\myT{2} % 2

\bye

There is nothing bad in splitting the working of a desired function into two or more macros; to the contrary, it is generally good practice.

5

Remember that tex is a macro processing language not a functional language. #1 is not an identifier that refers to a variable passed by reference to a function, it is simply a placeholder where the tokens supplied are inlined.

So whether it makes sense to assign anything to #1 depends what #1 is.

given

\def\myT#1%
  {%
    \ifnum#1=0
       #1=1 %HERE
    \fi
    #1 %THERE
  }
\myT{0} % 1

The line marked HERE is 0=1 so that is not an assignment, it simply typesets 0=1 However if you used the same definition but called it as

\newcount\zzz
\zzz=0
\myT{\zzz}

Then the line marked HERE would be \zzz=1 and would be an assignment, but the line marked THERE would be \zzz so would not typeset 1 it would need \the#1 which would be evaluated as \the\zzz

3

The process which might be closest to what you describe as "changing the value of an argument" is having the macro in question call itself again with the argument in question changed.

This programming-technique, where, depending on some condition, the last thing that a routine does, is terminating and calling itself again, is called tail-recursion.

In (La)TeX terminology: A (La)TeX macro is tail-recursive when, depending on some condition, the last tokens delivered as result of expanding it form another call to it.

When you need \if.. \else.. \fi-switches for checking conditions for the termination of the tail-recursion, make sure that tokens that form matching \else- and \fi-branches get processed/discarded from the input-buffer before the macro in question is called again. Otherwise the macro in question is not tail-recursive as the tokens forming these branches both remain and accumulate in the token-stream/in the input-buffer until being processed at a point in time when the recursion is already terminated, leading to taking unnecessary toll to the semantic nest, and also leading to unnecessary accumulation of tokens in the input-buffer.

You can, e.g., do something like this:

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
  \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
   {\myT{1}}{#1}%
}%
\myT{0}% 1

\myT{1}% 1

\myT{2}% 2
%...
\bye

In case you need to do more sophisticated changes, exchanging arguments after triggering the required expansion steps may be nice. Assume a macro which processes two arguments and where in case of the first argument having the value 0, that argument needs to be replaced by one with the value 1 and the second argument needs to be replaced by one where the value is increased by 1:

% this example requires eTeX-extensions (\numexpr):
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
\def\myT#1#2{%
  \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {%
    \expandafter\passfirsttosecond\expandafter{\number\numexpr#2+1\relax}{\myT{1}}%
  }{%
    Value of 1st arg: #1.\hfil\break
    Value of 2nd arg: #2.%
  }%
}%
\myT{0}{5}% Value of 1st arg: 1.\hfil\break Value of 2nd arg: 6.

\myT{1}{6}% Value of 1st arg: 1.\hfil\break Value of 2nd arg: 6.

\myT{2}{7}% Value of 1st arg: 2.\hfil\break Value of 2nd arg: 7.
%...
\bye

In case of needing to change more than two arguments, you can nest calls to \passfirsttosecond within \passfirsttosecond's second argument. If you do this, you get a pattern where changes of the last but k-th macro argument that needs to be changed will be performed right before carrying out the first but k-th \passfirsttosecond-directive:

Assume a macro which processes nine arguments and where in case of the first argument having the value 0, that argument needs to be replaced by one with the value 1 and each of the following arguments needs to be replaced by one where the value is increased by 1:

% this example requires eTeX-extensions (\numexpr):
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
\def\myT#1#2#3#4#5#6#7#8#9{%
  \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {%
    \expandafter\passfirsttosecond\expandafter{\number\numexpr#9+1\relax}{%
      \expandafter\passfirsttosecond\expandafter{\number\numexpr#8+1\relax}{%
        \expandafter\passfirsttosecond\expandafter{\number\numexpr#7+1\relax}{%
          \expandafter\passfirsttosecond\expandafter{\number\numexpr#6+1\relax}{%
            \expandafter\passfirsttosecond\expandafter{\number\numexpr#5+1\relax}{%
              \expandafter\passfirsttosecond\expandafter{\number\numexpr#4+1\relax}{%
                \expandafter\passfirsttosecond\expandafter{\number\numexpr#3+1\relax}{%
                  \expandafter\passfirsttosecond\expandafter{\number\numexpr#2+1\relax}{%
                    \expandafter\passfirsttosecond\expandafter{\number\numexpr#1+1\relax}{%
                      \myT
                    }%
                  }%
                }%
              }%
            }%
          }%
        }%
      }%
    }%
  }{%
    Value of 1st arg: #1.\hfil\break
    Value of 2nd arg: #2.\hfil\break
    Value of 3rd arg: #3.\hfil\break
    Value of 4th arg: #4.\hfil\break
    Value of 5th arg: #5.\hfil\break
    Value of 6th arg: #6.\hfil\break
    Value of 7th arg: #7.\hfil\break
    Value of 8th arg: #8.\hfil\break
    Value of 9th arg: #9.%
 }%
}%
\myT{0}{4}{9}{14}{19}{24}{29}{34}{39}%
%    Value of 1st arg: 1.\hfil\break
%    Value of 2nd arg: 5.\hfil\break
%    Value of 3rd arg: 10.\hfil\break
%    Value of 4th arg: 15.\hfil\break
%    Value of 5th arg: 20.\hfil\break
%    Value of 6th arg: 25.\hfil\break
%    Value of 7th arg: 30.\hfil\break
%    Value of 8th arg: 35.\hfil\break
%    Value of 9th arg: 40.%

\myT{1}{5}{10}{15}{20}{25}{30}{35}{40}%
%    Value of 1st arg: 1.\hfil\break
%    Value of 2nd arg: 5.\hfil\break
%    Value of 3rd arg: 10.\hfil\break
%    Value of 4th arg: 15.\hfil\break
%    Value of 5th arg: 20.\hfil\break
%    Value of 6th arg: 25.\hfil\break
%    Value of 7th arg: 30.\hfil\break
%    Value of 8th arg: 35.\hfil\break
%    Value of 9th arg: 40.%

\myT{2}{1}{2}{3}{4}{5}{6}{7}{8}%
%    Value of 1st arg: 2.\hfil\break
%    Value of 2nd arg: 1.\hfil\break
%    Value of 3rd arg: 2.\hfil\break
%    Value of 4th arg: 3.\hfil\break
%    Value of 5th arg: 4.\hfil\break
%    Value of 6th arg: 5.\hfil\break
%    Value of 7th arg: 6.\hfil\break
%    Value of 8th arg: 7.\hfil\break
%    Value of 9th arg: 8.%
%...
\bye

As you can see from the examples above, (La)TeX programming and especially (La)TeX's concept of "expansion" is not that much about assigning values to variables but is more about whirling up so-called tokens while you can take tokens for things/items that are placed in the token stream / input stream one behind the other.

The following analogy might be helpful when getting into touch with the concept of macro-expansion in (La)TeX:

When (La)TeX processes a .tex-input-file, i.e., some piece of (La)TeX source-code, it in the first stage takes that .tex-input-file for a set of instructions for inserting tokens into the token-stream. (These tokens may be character-tokens of whatsoever category-code, control-word-tokens and control-symbol-tokens.) In later stages, these tokens will be processed. During the stage of expansion, tokens will be removed/replaced by other tokens. During the stage of expansion, the order in which tokens appear in the token stream can be changed also.

In case you are interested in details about the rules which (La)TeX does follow for inserting tokens into the token-stream when reading the .tex-input-file, the discussion "Source Code Indentation" might be of interest to you.

In case you are interested in what kinds of tokens there are in (La)TeX, the discussion "What is the difference between 'macro' and 'command'?" might be of interest to you.

In case you are interested in how macro-arguments are handled in (La)TeX, the discussion "How does TeX look for delimited arguments?" might be of interest to you.

In case you are interested in expansion-trickery, the discussion "How can I know the number of expandafters when appending to a csname macro?" might be of interest to you.


As siracusa already pointed out you cannot assign another token sequence to a macro argument when (La)TeX has gathered that argument from the token stream.
But you can have (La)TeX examine that argument for "deciding" what tokens are to be delivered in the end. This can be done directly or by calling another macro where the arguments passed depend on the result of the examination.

Example for direct approach:

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
  \ifnum#1=0 %<- The space before the percent terminates \ifnum's second number, i.e., the number 0. It gets discarded silently.
    \expandafter\firstoftwo%<-\expandafter removes the tokens that form the else-branch and the \fi
  \else
    \expandafter\secondoftwo%<-\expandafter removes the \fi
  \fi
  {1}{#1}%
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2

Usually \romannumeral is used for obtaining tokens that form roman-notation of an integer number in lowercase letters. When due to \romannumeral searching a number, (La)TeX will keep expanding expandable tokens until either finding a number or raising an error-message. In case the number in question is not positive, (La)TeX will silently swallow it without delivering any token. Therefore you can (ab?)use \romannumeral for triggering a lot of expansion-work and argument-examining-work as long as you ensure that in the end (La)TeX finds a number which is not positive.

Another example for direct approach, invoking \romannumeral as a trigger for expansion so that it is ensured that the result of \myT in any case is delivered after two expansion-steps/after "hitting" the place where you find \myT by \expandafter twice:

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
   \romannunmeral0%<-While searching the number for \romannumneral, (La)TeX finds the digit-token 0 and keeps
                  %   triggering expansion while searching in the token-stream for
                  %   more digit-tokens or something that terminates the number.
   \ifnum#1=0 %<- The space before the percent terminates \ifnum's second number, i.e., the number 0. It gets discarded silently.
     \expandafter\firstoftwo%<-\expandafter removes the tokens that form the else-branch and the \fi
   \else
     \expandafter\secondoftwo%<-\expandafter removes the \fi
   \fi
   { 1}{ #1}% <- The space that precedes `1`/`#1` will be right
            %    behind the "0" from "\romannumeral0".
            %    Therefore it stops (La)TeX's seeking for digits for
            %    \rommanumeral which means that after doing
            %    expansion-work/argument-examining-work (La)TeX
            %    does only find the non-positive number "0" and 
            %    thus the result/effect of `\romannumeral` is:
            %    "Triggering doing a lot of expansion- and argument-
            %     examining-work while silently discarding the
            %     digit-token 0 which forms a non-positive number
            %     and the space behind it and not delivering
            %     anything in roman-notation at all."
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2

Example with calling another macro:

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\newcommand\myinnerT[1]{%
  Here \firstoftwo{\LaTeX}{} can do to the argument, #1, whatever shall be done.%
}%
\def\myT#1{%
  % Here the argument is examined for deciding how to call \myinnerT:
  \ifnum#1=0 %
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  {\myinnerT{1}}{\myinnerT{#1}}%
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2

Be aware that with the examples above checking whether the argument at some stage of expansion yields a number at all is not implemented. (Strictly spoken such a check is impossible in case the argument can consist of arbitrary token-sequences as in this case the tokens that form the argument might themselves form the (La)TeX-implementation of whatsoever algorithm and therefore such a check would (beneath other things) require solving the halting problem.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.