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
\ifnum`#1<`0 \isanumfalse
\else\ifnum`#1>`9 \isanumfalse
\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.
O pacote cite, de Donald Arseneau, oferece o seguinte teste para um argumento que seja um número inteiro estritamente positivo:
\def\IsPositive#1{%
TT\fi
\ifcat_\ifnum0<0#1 _\else A\fi
}
que pode ser adaptado para um teste para um inteiro não negativo, assim:
\def\IsNonNegative{%
\ifcat_\ifnum9<1#1 _\else A\fi
}
ou um teste para qualquer inteiro:
\def\gobbleminus#1{\ifx-#1\else#1\fi}
\def\IsInteger#1{%
TT\fi
\ifcat_\ifnum9<1\gobbleminus#1 _\else A\fi
}
mas isso certamente estende a técnica além do que seria razoável.
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:
\def\testnum#1{\afterassignment\testresult\count255=0#1 \end}
\def\testresult#1\end{\ifx\end#1\end\isanumtrue\else\isanumfalse\fi}
(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).
Em uma discussão posterior sobre o mesmo tópico, Michael Downes ofereceu:
\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
}
que conta com \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:
\if\IsInteger{}%
%
\else
%
\fi
O classe LaTeX memoir tem seu próprio comando interno, o
\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:
\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
}
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!).
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!