假设一个宏,它没有根据 定义\long
,并且没有根据 定义\outer
,并且没有根据 定义,\protected
并且不处理参数,并且其顶层扩展仅产生不是的标记,其中\outer
一些是类别代码 6(参数)的显式字符标记:
\def\macro{This is some hashes: ##}
是否有一些\edef
————技巧可以重新定义以提供完全相同的一组标记\unexpanded
,但类别代码 6(参数)的显式字符标记加倍,就像您定义的那样\expanded
\macro
\def\macro{This is some hashes: ####}
?
假设构成替换文本的标记\macro
只能通过扩展才能获得\macro
。
我自己对此事的处理方式是使用\romannumeral
扩展驱动的递归循环来迭代参数,将类别代码 6(参数)的每个显式字符标记增加四倍。
(增加四倍而不是增加一倍,因为扩展\macro
意味着将连续哈希的数量减半......)
我不认为我的日常生活\QuadrupleEveryHash
很优雅:
作为副作用,该例程确实通过匹配 catcode 1 和 2 的花括号对来替换匹配的 catcode 1 和 2 的显式字符标记对。(我想这在大多数情况下不会是问题,因为通常花括号是类别代码 1/2 的唯一字符......)
该例程需要
\detokenize
从 e-TeX 扩展中检查给定的标记是否为哈希。(\string#
产生单个标记,而产生两个连续的标记……)#12
\detokenize{#}
#12
每次找到左括号时,
\romannumeral
就会启动另一个由扩展驱动的例程实例,如果替换文本包含\macro
大量括号嵌套,则会对语义嵌套造成损害。\macro
该例程仅在的替换文本不包含\outer
-tokens(可能就是的情况)时才有效\def\macro{\foo and hash ##.}\outer\def\foo{Now foo is outer.}
。
\catcode`\@=11
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo,
%% \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%% \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%% \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\long\def\UD@firstoftwo#1#2{#1}%
\long\def\UD@secondoftwo#1#2{#2}%
\long\def\UD@PassFirstToSecond#1#2{#2{#1}}%
\long\def\UD@Exchange#1#2{#2#1}%
\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\long\def\UD@CheckWhetherNull#1{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
{\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
\long\def\UD@CheckWhetherBrace#1{%
\romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
{\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument's first token is an explicit
%% space token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\long\def\UD@CheckWhetherLeadingSpace#1{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%.............................................................................
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\long\def\UD@ExtractFirstArg#1{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\long\def\UD@ExtractFirstArgLoop#1{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% \QuadrupleEveryHash{<argument>}%
%%
%% Each explicit catcode-6(parameter)-character-token of the <argument>
%% will be quadrupled.
%%
%% You obtain the result after two expansion-steps, i.e.,
%% in expansion-contexts you get the result after "hitting"
%% \QuadrupleEveryHash by two \expandafter.
%%
%% As a side-effect, the routine does replace matching pairs of explicit
%% character tokens of catcode 1 and 2 by matching pairs of curly braces
%% of catcode 1 and 2.
%% I suppose this won't be a problem in most situations as usually the
%% curly braces are the only characters of category code 1 / 2...
%%
%% This routine needs \detokenize from the eTeX extensions.
%%-----------------------------------------------------------------------------
\long\def\QuadrupleEveryHash#1{%
\romannumeral0\UD@QuadrupleEveryHashLoop{#1}{}%
}%
\long\def\UD@QuadrupleEveryHashLoop#1#2{%
\UD@CheckWhetherNull{#1}{ #2}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@QuadrupleEveryHashLoop
\expandafter{\UD@removespace#1}{#2 }%
}{%
\UD@CheckWhetherBrace{#1}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0%
\expandafter\UD@QuadrupleEveryHashLoop
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{}%
}{#2}}%
{\expandafter\UD@QuadrupleEveryHashLoop
\expandafter{\UD@firstoftwo{}#1}}%
}{%
\expandafter\UD@CheckWhetherHash
\romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{#1}{#2}%
}%
}%
}%
}%
\long\def\UD@CheckWhetherHash#1#2#3{%
\expandafter\UD@CheckWhetherLeadingSpace\expandafter{\string#1}{%
% The very edge case of probably having a space of catcode 6:
\expandafter\expandafter\expandafter\UD@CheckWhetherNull
\expandafter\expandafter\expandafter{%
\expandafter\UD@removespace\string#1}{%
\expandafter\expandafter\expandafter\UD@CheckWhetherNull
\expandafter\expandafter\expandafter{%
\expandafter\UD@removespace\detokenize{#1}}{%
% no hash
\UD@secondoftwo
}{% hash
\UD@firstoftwo
}%
}%
}{%
% The case of probably having a non-space of catcode 6:
\expandafter\expandafter\expandafter\UD@CheckWhetherNull
\expandafter\expandafter\expandafter{%
\expandafter\UD@firstoftwo
\expandafter{\expandafter}\string#1}{%
\expandafter\expandafter\expandafter\UD@CheckWhetherNull
\expandafter\expandafter\expandafter{%
\expandafter\UD@firstoftwo
\expandafter{\expandafter}\detokenize{#1}}{%
% no hash
\UD@secondoftwo
}{% hash
\UD@firstoftwo
}%
}%
}%
{%no hash
\UD@secondoftwo
}%
{% hash
\expandafter\UD@QuadrupleEveryHashLoop
\expandafter{\UD@firstoftwo{}#2}{#3#1#1#1#1}%
}{% no hash
\expandafter\UD@QuadrupleEveryHashLoop
\expandafter{\UD@firstoftwo{}#2}{#3#1}%
}%
}%
%%\catcode`\@=12
%%=============================================================================
\tt\frenchspacing
1234567890123456789012345678901234567890123456789012345678901234567890
\def\macro{This is some hashes: ##}
\expandafter\def
\expandafter\Macro
\expandafter{%
\romannumeral0\UD@Exchange{ }{%
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\expandafter
}%
\expandafter\QuadrupleEveryHash\expandafter{\macro}%
}%
\string\macro: \meaning\macro
\string\Macro: \meaning\Macro
\bigskip
Now the edge case of spaces of category code 6(parameter) being part of \string\macro's\break
replacement-text:
\bigskip
1234567890123456789012345678901234567890123456789012345678901234567890
\begingroup
\catcode`\ =6\relax%
\gdef\macro{This Is Some Hashes: ##}%
\endgroup%
\expandafter\def
\expandafter\Macro
\expandafter{%
\romannumeral0\UD@Exchange{ }{%
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\expandafter
}%
\expandafter\QuadrupleEveryHash\expandafter{\macro}%
}%
\string\macro: X\meaning\macro X
\string\Macro: X\meaning\Macro X
\bye
答案1
每种方法\def...{body}
都会将正文内部表示中的井号数量减半。每种方法\toks<num>={body}
都会保留井号数量。每种方法\write
都会\scantokens
将井号数量翻倍。
\def\macro{This is some hashes: ##} % we have one # in internal representation
\scantokens\expandafter{\expandafter\toks\expandafter0\expandafter{\macro}}
% \scantotkens does \write, we have two internal #, \toks0 keeps two #.
\edef\newmacro{\the\toks0} % this keeps two # in internal representation
\message{\meaning\macro, \meaning\newmacro} % \meaning doubles the #, so we see:
% ## ####
% because there is:
% # ## in internal representation
\bye
答案2
您可以\edef
使用\unexpanded
:
\documentclass{article}
\de\documentclass{article}
\def\safedef#1#2{\edef#1{\unexpanded{#2}}}
\begin{document}
\def\macro{This is some hashes: ##}
\safedef\Macro{This is some hashes: ##}
\typeout{\macro -\Macro}
\end{document}
节目
这是一些哈希:##-这是一些哈希:####
答案3
和expl3
:
\documentclass{article}
\ExplSyntaxOn
\cs_new_protected:Npn \doublehashes #1 #2
{
\tl_set_eq:NN \l_tmpa_tl #2
\regex_replace_all:nnN { \cP. } { \cP\#\cP\# } \l_tmpa_tl
\tl_set_eq:NN #1 \l_tmpa_tl
}
\ExplSyntaxOff
\def\macro{This is some hashes: ####}
\doublehashes\Macro\macro
\frenchspacing
\begin{document}
\texttt{\string\macro=\meaning\macro}
\texttt{\string\Macro=\meaning\Macro}
\end{document}
当然,您知道,它\meaning
会为在替换文本中找到的每个井号打印两个井号。因此\macro
有两个井号,并且\Macro
有四个井号。
答案4
一种etl
基于的方法。这将类别代码 1 和 2 标记规范化为括号 ( {}
),否则应符合标准。
\documentclass{article}
\usepackage{etl}
\ExplSyntaxOn
\cs_generate_variant:Nn \etl_token_replace_all_deep:nNn { V }
\NewDocumentCommand \doublehashes { m m }
{ \cs_set_nopar:Npe #1 { \etl_token_replace_all_deep:VNn #2 ## { #### } } }
\ExplSyntaxOff
\def\macro{This is some hashes: ####}
\doublehashes\Macro\macro
\begin{document}
\texttt{\string\macro=\meaning\macro}
\texttt{\string\Macro=\meaning\Macro}
\end{document}