Situação

Situação

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 \detokenizepara 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 \foois \def\baz#1{-#1-}e uma chamada de \fooserão definidos \bazcomo 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 \detokenizequem 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 \edefserá 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}

insira a descrição da imagem aqui

Uma definição diferente para \lineofhashsignsisso 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 \detokenizeprimitivo de fato significará que #não é tratado como um parâmetro, mas quando expandido. Lembre-se que em uma atribuição normal ( \newcommandem LaTeX, \defcomo uma primitiva TeX) nenhuma expansão ocorre. Aqui, \newenvironmentestá 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 \newenvironmentfinalmente 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

informação relacionada