Como alterar o valor de um argumento (por exemplo, nº 1) de modo que qualquer referência a ele (o argumento nº 1) se expanda para o novo valor?

Como alterar o valor de um argumento (por exemplo, nº 1) de modo que qualquer referência a ele (o argumento nº 1) se expanda para o novo valor?

Eu queria saber como alterar #1, com base em seu valor, para outra coisa, de modo que qualquer coisa #1que siga a alteração contenha o valor modificado, não o valor original (passado de fora para a macro).

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

EDIT (solução alternativa; prefiro alterar #1o valor de diretamente, como #1=1, do que expandir um \myTargumento aninhado de 1, como abaixo):

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

Responder1

Você não pode “atribuir” valores a #1não ser usando o argumento apropriado.

O tipo de tarefa que você apresenta é realizado de forma mais conveniente com duas macros; embora você possa chamar \myT{1}quando o argumento original for 0, isso é ineficiente, pois executa novamente uma verificação que já foi feita.

\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

Não há nada de errado em dividir o funcionamento de uma função desejada em duas ou mais macros; pelo contrário, é geralmente uma boa prática.

Responder2

Lembre-se de que tex é uma linguagem de processamento de macro e não uma linguagem funcional. #1não é um identificador que se refere a uma variável passada por referência a uma função, é simplesmente um espaço reservado onde os tokens fornecidos são embutidos.

Portanto, se faz sentido atribuir algo #1depende do que #1é.

dado

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

A linha marcada AQUI 0=1não é uma atribuição, ela simplesmente compõe 0=1 No entanto, se você usou a mesma definição, mas a chamou como

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

Então a linha marcada AQUI seria \zzz=1e seria uma atribuição, mas a linha marcada LÁ seria \zzzentão não seria digitada, 1seria necessária, \the#1o que seria avaliado como\the\zzz

Responder3

O processo que pode estar mais próximo do que você descreve como "alterar o valor de um argumento" é fazer com que a macro em questão se chame novamente com o argumento em questão alterado.

Essa técnica de programação, onde, dependendo de alguma condição, a última coisa que uma rotina faz é encerrar e chamar a si mesma novamente, é chamada de recursão final.

Na terminologia (La)TeX: Uma macro (La)TeX é recursiva quando, dependendo de alguma condição, os últimos tokens entregues como resultado de sua expansão formam outra chamada para ela.

Quando você precisar \if.. \else.. \fide -switches para verificar as condições para o término da recursão final, certifique-se de que os tokens que formam os ramos \else- e \fi-correspondentes sejam processados/descartados do buffer de entrada antes que a macro em questão seja chamada novamente. Caso contrário, a macro em questão não é recursiva de cauda, ​​pois os tokens que formam essas ramificações permanecem e se acumulam no fluxo de tokens/no buffer de entrada até serem processados ​​em um momento em que a recursão já foi encerrada, levando a tomadas desnecessárias. pedágio para o ninho semântico e também levando ao acúmulo desnecessário de tokens no buffer de entrada.

Você pode, por exemplo, fazer algo assim:

\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

Caso você precise fazer alterações mais sofisticadas, trocar argumentos após acionar as etapas de expansão necessárias pode ser interessante. Suponha uma macro que processa dois argumentos e onde no caso do primeiro argumento ter o valor 0, esse argumento precisa ser substituído por um com o valor 1 e o segundo argumento precisa ser substituído por outro onde o valor é aumentado em 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

Caso seja necessário alterar mais de dois argumentos, você pode aninhar chamadas \passfirsttoseconddentro \passfirsttoseconddo segundo argumento. Se você fizer isso, obterá um padrão onde as alterações do último mas k-ésimo argumento da macro que precisa ser alterado serão executadas logo antes de executar a primeira mas k-ésima \passfirsttoseconddiretiva:

Suponha uma macro que processa nove argumentos e onde no caso do primeiro argumento ter o valor 0, esse argumento precisa ser substituído por um com o valor 1 e cada um dos argumentos seguintes precisa ser substituído por um onde o valor é aumentado em 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

Como você pode ver nos exemplos acima, a programação (La)TeX e especialmente o conceito de "expansão" do (La)TeX não se trata tanto de atribuir valores a variáveis, mas sim de criar os chamados tokens enquanto você pode pegar tokens para coisas/itens que são colocados no fluxo de token/fluxo de entrada um atrás do outro.

A seguinte analogia pode ser útil ao entrar em contato com o conceito de macroexpansão em (La)TeX:

Quando o (La)TeX processa um arquivo de entrada .tex, ou seja, algum pedaço de código-fonte do (La)TeX, no primeiro estágio ele pega esse arquivo de entrada .tex para um conjunto de instruções para inserir tokens no fluxo de token. (Esses tokens podem ser tokens de caracteres de qualquer código de categoria, tokens de palavras de controle e tokens de símbolos de controle.) Em estágios posteriores, esses tokens serão processados. Durante a fase de expansão, os tokens serão removidos/substituídos por outros tokens. Durante o estágio de expansão, a ordem em que os tokens aparecem no fluxo de tokens também pode ser alterada.

Caso você esteja interessado em detalhes sobre as regras que o (La)TeX segue para inserir tokens no token-stream ao ler o arquivo de entrada .tex, a discussão"Recuo do código-fonte"pode ser do seu interesse.

Caso você esteja interessado em saber quais tipos de tokens existem no (La)TeX, a discussão"Qual é a diferença entre 'macro' e 'comando'?"pode ser do seu interesse.

Caso você esteja interessado em como os macro-argumentos são tratados no (La)TeX, a discussão"Como o TeX procura argumentos delimitados?"pode ser do seu interesse.

Caso você esteja interessado em truques de expansão, a discussão"Como posso saber o número de expansões ao anexar a uma macro csname?"pode ser do seu interesse.


Como siracusa já apontou, você não pode atribuir outra sequência de token a um argumento macro quando (La)TeX reuniu esse argumento do fluxo de token.
Mas você pode fazer com que o (La)TeX examine esse argumento para "decidir" quais tokens serão entregues no final. Isto pode ser feito diretamente ou chamando outra macro onde os argumentos passados ​​dependem do resultado do exame.

Exemplo de abordagem direta:

\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

Geralmente \romannumeralé usado para obter tokens que formam a notação romana de um número inteiro em letras minúsculas. Quando for necessário \romannumeralpesquisar um número, o (La)TeX continuará expandindo tokens expansíveis até encontrar um número ou gerar uma mensagem de erro. Caso o número em questão não seja positivo, o (La)TeX irá engoli-lo silenciosamente sem entregar nenhum token. Portanto, você pode (ab?) usar \romannumeralpara desencadear muito trabalho de expansão e trabalho de exame de argumentos, desde que garanta que no final (La)TeX encontre um número que não seja positivo.

Outro exemplo de abordagem direta, invocando \romannumeralcomo gatilho para expansão para que seja garantido que o resultado de \myTem qualquer caso seja entregue após duas etapas de expansão/após "acertar" o local onde você encontra \myTduas \expandaftervezes:

\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

Exemplo com chamada de outra 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

Esteja ciente de que, com os exemplos acima, verificar se o argumento em algum estágio da expansão produz um número não é implementado. (Estritamente falando, tal verificação é impossível caso o argumento possa consistir em sequências de tokens arbitrárias, pois neste caso os tokens que formam o argumento podem formar a implementação (La)TeX de qualquer algoritmo e, portanto, tal verificação seria (abaixo outras coisas) exigem resolver oproblema de parada.)

informação relacionada