A própria análise lexical do TeX não oferece muito suporte ao programador de macro: enquanto os códigos de categoria distinguem letras (ou o que o TeX considera como letras) de todo o resto, não há suporte para a análise de números.
A solução simples é comparar caracteres numéricos com os caracteres do argumento, um por um, por uma sequência de testes diretos, e declarar que o argumento “não é um número” se algum caractere falhar em todas as comparações:
que se poderia, então, usar em uma macro recursiva de cauda para engolir um argumento. Pode-se fazer um pouco melhor assumindo (com segurança) que os códigos de caracteres dos dígitos são consecutivos:\ifx1#1 \else\ifx2#1 ... \else\ifx9#1 \else\isanumfalse \fi\fi...\fi
novamente usado em recursão de cauda. No entanto, essas formas não são muito satisfatórias: fazer a recursão “corretamente” é problemático (ela tem a tendência de engolir espaços no argumento) e, de qualquer forma, o próprio TeX tem mecanismos para ler números, e seria bom usá-los.\ifnum`#1<`0 \isanumfalse \else\ifnum`#1>`9 \isanumfalse \fi \fi
O pacote cite, de Donald Arseneau, oferece o seguinte teste para um argumento que seja um número inteiro estritamente positivo:
que pode ser adaptado para um teste para um inteiro não negativo, assim:\def\IsPositive#1{% TT\fi \ifcat_\ifnum0<0#1 _\else A\fi }
ou um teste para qualquer inteiro:\def\IsNonNegative{% \ifcat_\ifnum9<1#1 _\else A\fi }
mas isso certamente estende a técnica além do que seria razoável.\def\gobbleminus#1{\ifx-#1\else#1\fi} \def\IsInteger#1{% TT\fi \ifcat_\ifnum9<1\gobbleminus#1 _\else A\fi }
Se não nos importarmos com o sinal, podemos usar o TeX para remover o número todo (sinal e tudo) do fluxo de entrada e, em seguida, observar o que restou:
(cuja técnica é devida a David Kastrup; o truque para evitar os erros observados em uma versão anterior desta resposta, foi sugerido por Andreas Matthias).\def\testnum#1{\afterassignment\testresult\count255=0#1 \end} \def\testresult#1\end{\ifx\end#1\end\isanumtrue\else\isanumfalse\fi}
Em uma discussão posterior sobre o mesmo tópico, Michael Downes ofereceu:
que conta com\def\IsInteger#1{% TT\fi \begingroup \lccode`\-=`\0 \lccode`+=`\0 \lccode`\1=`\0 \lccode`\2=`\0 \lccode`\3=`\0 \lccode`\4=`\0 \lccode`\5=`\0 \lccode`\6=`\0 \lccode`\7=`\0 \lccode`\8=`\0 \lccode`\9=`\0 \lowercase{\endgroup \expandafter\ifx\expandafter\delimiter \romannumeral0\string#1}\delimiter }
\romannumeral
para produzir um resultado vazio se seu argumento for zero. É uma pena que essa técnica tenha a infeliz propriedade de aceitar expressões simples como ‘1+2-3’; isso poderia ser resolvido por uma construção inicial similar a \gobbleminus
.
Todas as funções completas acima são projetadas para serem usadas em condicionais do TeX escritas “ynaturalmente” — por exemplo:
O classe LaTeX memoir tem seu próprio comando interno, o\if\IsInteger{}% % \else % \fi
\checkifinteger
{num}
, que ajusta o comando condicional
\ifinteger
se o argumento for um inteiro.
É claro que toda essa confusão seria (essencialmente) desnecessária se houvesse um meio simples de “pegar” erros do TeX. Imaginando um primitivo para pegar erros \ifnoerror
, poderíamos escrever:
usando, assim, o próprio código de análise de inteiros do TeX para fazer a verificação. É uma pena que tal mecanismo nunca tenha sido definido (talvez seja impossível programa-lo dentro do TeX!).\def\IsInteger#1{% TT% \ifnoerror \tempcount=#1\relax % carries on if no error \expandafter\iftrue \else % here if there was an error \expandafter\iffalse \fi }
This question on the Web: http://latex.net.br/faq/FAQ-isitanum.html
Do you have any question? Ask on: latex.net.br - we love qood questions!