Как изменить значение аргумента (например, #1) так, чтобы любая ссылка на него (аргумент, #1) расширялась до нового значения?

Как изменить значение аргумента (например, #1) так, чтобы любая ссылка на него (аргумент, #1) расширялась до нового значения?

Мне было интересно, как изменить #1, основываясь на его значении, на что-то другое, чтобы все, #1что следует за изменением, содержало измененное значение, а не исходное значение (переданное макросу извне).

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

EDIT (обходной путь; лучше изменить #1значение напрямую, как #1=1, чем расширять вложенный \myTс аргументом 1, как показано ниже):

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

решение1

Вы не можете «присвоить» значения другим, #1кроме как с использованием соответствующего аргумента.

Тип задачи, которую вы представляете, удобнее выполнять с помощью двух макросов; хотя вы можете вызвать его, \myT{1}когда исходный аргумент равен 0, это неэффективно, поскольку повторно выполняет проверку, которая уже была выполнена.

\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

Нет ничего плохого в разделении работы нужной функции на два или более макроса; наоборот, это, как правило, хорошая практика.

решение2

Помните, что tex — это язык обработки макросов, а не функциональный язык. #1Это не идентификатор, ссылающийся на переменную, переданную по ссылке в функцию, это просто заполнитель, в который встраиваются предоставленные токены.

Так что имеет ли смысл что-либо назначать, #1зависит от того, что #1именно.

данный

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

Строка, помеченная ЗДЕСЬ, 0=1не является присваиванием, она просто устанавливает 0=1. Однако, если бы вы использовали то же определение, но назвали его

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

Тогда строка, отмеченная ЗДЕСЬ, будет \zzz=1и будет заданием, но строка, отмеченная ТАМ, будет \zzzтак, что не будет набрана, 1она будет нужна \the#1, что будет оценено как\the\zzz

решение3

Процесс, который, возможно, наиболее близок к тому, что вы описываете как «изменение значения аргумента», заключается в том, что рассматриваемый макрос снова вызывает себя с измененным рассматриваемым аргументом.

Этот метод программирования, при котором в зависимости от некоторого условия последнее, что делает процедура, — это завершает работу и снова вызывает саму себя, называется хвостовой рекурсией.

В терминологии (La)TeX: макрос (La)TeX является хвостово-рекурсивным, когда, в зависимости от некоторого условия, последние токены, доставленные в результате его расширения, формируют другой его вызов.

Когда вам нужны \if.. \else.. \fi-switches для проверки условий завершения хвостовой рекурсии, убедитесь, что токены, которые формируют соответствующие \else- и \fi-ветви, обрабатываются/отбрасываются из входного буфера до того, как рассматриваемый макрос будет вызван снова. В противном случае рассматриваемый макрос не является хвостовым рекурсивным, поскольку токены, формирующие эти ветви, остаются и накапливаются в потоке токенов/входном буфере до тех пор, пока не будут обработаны в момент времени, когда рекурсия уже завершена, что приводит к ненужной нагрузке на семантическое гнездо, а также к ненужному накоплению токенов во входном буфере.

Например, можно сделать что-то вроде этого:

\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

В случае, если вам нужно сделать более сложные изменения, обмен аргументами после запуска требуемых шагов расширения может быть хорошим. Предположим, что есть макрос, который обрабатывает два аргумента, и в случае, если первый аргумент имеет значение 0, этот аргумент необходимо заменить на аргумент со значением 1, а второй аргумент необходимо заменить на аргумент, в котором значение увеличивается на 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

В случае необходимости изменить более двух аргументов, вы можете вложить вызовы во \passfirsttosecondвторой \passfirsttosecondаргумент . Если вы сделаете это, вы получите шаблон, в котором изменения последнего, но k-го, аргумента макроса, который необходимо изменить, будут выполнены непосредственно перед выполнением первой, но k-й \passfirsttosecondдирективы:

Предположим, что макрос обрабатывает девять аргументов, и в случае, если первый аргумент имеет значение 0, этот аргумент необходимо заменить на аргумент со значением 1, а каждый из следующих аргументов необходимо заменить на аргумент, значение которого увеличено на 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

Как видно из приведенных выше примеров, программирование (La)TeX и особенно концепция «расширения» в (La)TeX заключается не столько в присвоении значений переменным, сколько в создании так называемых токенов, в то время как токены можно использовать для вещей/элементов, которые помещаются в поток токенов/входной поток один за другим.

Следующая аналогия может оказаться полезной при знакомстве с концепцией макрорасширения в (La)TeX:

Когда (La)TeX обрабатывает .tex-input-file, т. е. некоторую часть исходного кода (La)TeX, на первом этапе он принимает этот .tex-input-file за набор инструкций для вставки токенов в поток токенов. (Эти токены могут быть символьными токенами любой категории-кода, токены-управляющих-слов и токены-управляющих-символов.) На более поздних этапах эти токены будут обработаны. На этапе расширения токены будут удалены/заменены другими токенами. На этапе расширения также может быть изменен порядок, в котором токены появляются в потоке токенов.

Если вас интересуют подробности о правилах, которым (La)TeX следует при вставке токенов в поток токенов при чтении файла .tex-input-file, обсуждение«Отступ исходного кода»может быть вам интересно.

Если вам интересно, какие виды токенов есть в (La)TeX, обсуждение«В чем разница между «макросом» и «командой»?»может быть вам интересно.

Если вам интересно, как обрабатываются макроаргументы в (La)TeX, обсуждение«Как TeX ищет разделенные аргументы?»может быть вам интересно.

Если вас интересуют трюки с расширением, обсуждение«Как узнать количество Expandafters при добавлении к макросу csname?»может быть вам интересно.


Как уже отметил siracusa, вы не можете назначить другую последовательность токенов аргументу макроса, когда (La)TeX собрал этот аргумент из потока токенов.
Но вы можете заставить (La)TeX проверить этот аргумент для "решения", какие токены должны быть доставлены в конце. Это можно сделать напрямую или вызвав другой макрос, где переданные аргументы зависят от результата проверки.

Пример прямого подхода:

\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

Обычно \romannumeralиспользуется для получения токенов, которые образуют римскую запись целого числа строчными буквами. При \romannumeralпоиске числа (La)TeX будет продолжать расширять расширяемые токены, пока не найдет число или не выдаст сообщение об ошибке. В случае, если число в вопросе не является положительным, (La)TeX молча проглотит его, не доставив никаких токенов. Поэтому вы можете (ab?)использовать \romannumeralдля запуска большого количества работы по расширению и проверке аргументов, пока вы гарантируете, что в конце (La)TeX найдет число, которое не является положительным.

Другой пример прямого подхода, вызывающий \romannumeralв качестве триггера для расширения, чтобы гарантировать, что результат \myTв любом случае будет доставлен после двух шагов расширения/после «попадания» в место, где вы найдете, \myTдважды \expandafter:

\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

Пример вызова другого макроса:

\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

Имейте в виду, что в приведенных выше примерах проверка того, выдает ли аргумент на каком-либо этапе разложения число, вообще не реализована. (Строго говоря, такая проверка невозможна, если аргумент может состоять из произвольных последовательностей токенов, поскольку в этом случае токены, образующие аргумент, сами могут образовывать (La)TeX-реализацию любого алгоритма, и поэтому такая проверка (помимо прочего) потребует решенияпроблема остановки.)

Связанный контент