
Quando uso o sinal de libra/número/sinal de hash#
dentro de comandos e ambientes, isso se refere a argumentos. No entanto, não deveria \detokenize{#}
fazer issoNÃOum argumento?
Situação
O sinal de libra é ótimo para dividir arquivos de log.
Exemplo
Tente descomentar o segundo \detokenize
para obter um erro.
\documentclass{article}
\usepackage{fontspec}% compile with xelatex
\newenvironment{detokenizetest}[1]
{% firstoftwo
\detokenize{##############################################################################}%
%\detokenize{##BEGIN#######################################################################}% uncommenting this causes a compile error
}%
{% secondoftwo
}%
\begin{document}
\null
\end{document}
Saída de erro
Este erro ocorre apenas ao descomentar a linha que contém BEGIN
.
! Illegal parameter number in definition of \detokenizetest.
<to be read again>
}
l.9 }
%
?
Responder1
Desde#
possuir código de categoria 6, tem um significado muito especial no TeX, pois é utilizado para denotar parâmetros em definições de macro.
As regras do TeX dizem que quando você quiserlojaa #
no texto de substituição de uma macro, você precisa dobrá-lo.
Ao processar o texto de substituição para uma definição de macro, um único #
deve ser seguido por um dígito (1 a 9), denotando um ponteiro para o argumento correspondente, enquanto ##
será armazenado como #
. Este é o truque que permite algo como
\def\foo{\def\baz##1{-##1-}}
portanto, o texto de substituição de \foo
is \def\baz#1{-#1-}
e uma chamada de \foo
serão definidos \baz
como uma macro de argumento único.
O primitivo\detokenize
no texto de substituição fará o seu trabalhoquando a macro é chamada e expandida, não no momento da definição, a menos que a definição seja feita com \edef
, que primeiro realizará a expansão completa antes de armazenar o texto de substituição na memória.
Observe que quando você fizer isso \meaning\foo
(assumindo a definição acima), você será presenteado com
macro:->\def \baz ##1{-##1-}
e o mesmo acontecerá com \detokenize
quem utiliza o mesmo mecanismo. Assim algo como
\edef\hashmarks{\detokenize{####}}
na verdade, veremos dois #
no texto de substituição, mas o resultado final será quatro deles. O mecanismo padrão durante o processamento \edef
será primeiro metade do número #
e depois\detokenize
duplicará. Em particular, vocênão podeproduza desta forma um número ímpar de #
.
Se você quiser produzir linhas delimitadoras, é muito melhor usar um método indireto, fazendo antecipadamente a lista de tokens necessária composta #
pelo código de categoria 12. No exemplo eu uso três deles, só porque é um número ímpar.
\documentclass{article}
\begingroup\lccode`?=`# \lowercase{\endgroup
\newcommand{\lineofhashsigns}{???}
}
\newenvironment{delimitedtext}
{\par\lineofhashsigns\par}
{\par\lineofhashsigns\par}
\begin{document}
\begin{delimitedtext}
abc
\end{delimitedtext}
\end{document}
Uma definição diferente para \lineofhashsigns
isso não exige \lowercase
é alterar o código da categoria:
\catcode`#=12
\newcommand{\lineofhashsigns}{###}
\catcode`#=6
Você pode adicionar qualquer token no texto de substituição, desde que não queira que a macro tenha argumentos.
Responder2
O \detokenize
primitivo de fato significará que #
não é tratado como um parâmetro, mas quando expandido. Lembre-se que em uma atribuição normal ( \newcommand
em LaTeX, \def
como uma primitiva TeX) nenhuma expansão ocorre. Aqui, \newenvironment
está executando exatamente o mesmo 'apenas armazenar' os tokens que \newcommand
. Isso significa que temos a regra usual do TeX de que, na criação de uma macro, temos que ter os parâmetros correspondentes para cada #
presente ou temos que #
duplicar os tokens.
Como \newenvironment
finalmente resolve para a \def
, podemos fazer o que quiser usando \edef
:
\edef\detokenizetest#1{%
\detokenize{##############################################################################}%
\detokenize{##BEGIN#######################################################################}%
}
\def\enddetokenizetest{}% As \newenvironment will do this
\show\detokenizetest