将参数扩展为字符串

将参数扩展为字符串

我想构建一个 TeX 宏,通过将每个数字扩展为字母序列(始终是同一个字母)来转换其参数。例如3(2)2(1)应该生成AAA(AA)AA(A)

A我已经找到了一种从一个数字中产生任意数字的方法:

\newcommand\expandtoA[1]{%
  \foreach \index in {1, ..., #1} {A}
}

因此,\expandtoA{10}将扩展为AAAAAAAAAA

但是,我正在寻找一种方法来找到参数中的所有数字,并用它所代表的 A 的数量替换每个数字。

答案1

这确实可以通过扩展来实现,如图所示,它\typeout与排版段落一样有效。



\documentclass{article}

\def\zz#1{\zzz#1(\relax)}
\def\zzz#1(#2){\zzA{#1}\ifx\relax#2\else(\zzA{#2})\expandafter\zzz\fi}
\def\zzA#1{\ifnum\numexpr0#1\relax>0 A\expandafter\zzA\expandafter{\the\numexpr#1-1\relax}\fi}

\begin{document}

\zz{3(2)2(1)}

\typeout{\zz{3(2)2(1)}}

\end{document}

放置

AAA(AA)AA(A)

在终端上。

答案2

这是一个使用 的可扩展方法expl3\Replicate[<tokens>]{<token list>}它将循环遍历您的输入<token list>,寻找任何数字序列,并用输入的重复次数替换它们<tokens>(默认情况下A)。除数字之外的所有内容都将转发到输出,因此您可以得到类似

\Replicate{3(2)2(1)}
\Replicate[Z]{3(2)2(1)}
\Replicate[(Z)]{3(2)2(1)}
\Replicate[Z]{3 \textbf{(2) 2}( 1 )}
\edef\zzzzzzzzzzzz{\Replicate[Z]{3\textbf{(2)2}(1)}}
\texttt{\meaning\zzzzzzzzzzzz}

生产:

在此处输入图片描述


代码如下:

\documentclass{article}
\usepackage{xparse}
\pagestyle{empty}
\ExplSyntaxOn
\NewExpandableDocumentCommand \Replicate { O{A} m }
  { \perror_replicate:nn {#1} {#2} }
\cs_new:Npn \perror_replicate:nn #1 #2
  {
    \__perror_replicate_loop:w #2
      \q_recursion_tail \q_recursion_stop {#1} { }
  }
\cs_new:Npn \__perror_replicate_loop:w #1 \q_recursion_stop
  {
    \tl_if_head_is_N_type:nTF {#1}
      { \__perror_replicate_parse_token:N }
      {
        \tl_if_head_is_group:nTF {#1}
          { \__perror_replicate_nested:n }
          { \__perror_replicate_output_space:w }
      }
    #1 \q_recursion_stop
  }
\use:nn { \cs_new:Npn \__perror_replicate_output_space:w } { ~ }
  { \__perror_replicate_output:nw { ~ } }
\cs_new:Npn \__perror_replicate_output:nw #1 #2 \q_recursion_stop #3 #4
  { \__perror_replicate_loop:w #2 \q_recursion_stop {#3} { #4 #1 } }
\cs_new:Npn \__perror_replicate_nested:n #1 #2 \q_recursion_stop #3
  {
    \exp_args:Ne \__perror_replicate_output:nw
      { { \perror_replicate:nn {#3} {#1} } }
    #2 \q_recursion_stop {#3}
  }
\cs_new:Npn \__perror_replicate_end:nn #1 #2 { \exp_not:n {#2} }
\cs_new:Npn \__perror_replicate_parse_token:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1
      { \__perror_replicate_end:nn }
    \__perror_replicate_if_digit:NTF #1
      { \__perror_replicate_collect_number:nw }
      { \__perror_replicate_output:nw }
    {#1}
  }
\prg_new_conditional:Npnn \__perror_replicate_if_digit:N #1 { TF }
  {
    \if_int_compare:w 10 < 9 \token_to_str:N #1 \exp_stop_f:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
\cs_new:Npn \__perror_replicate_collect_number:nw #1 #2 \q_recursion_stop
  {
    \tl_if_head_is_N_type:nTF {#2}
      { \__perror_replicate_collect_number:nN }
      { \__perror_replicate_finish_number:nw }
    {#1} #2 \q_recursion_stop
  }
\cs_new:Npn \__perror_replicate_collect_number:nN #1 #2
  {
    \quark_if_recursion_tail_stop_do:Nn #2
      {
        \__perror_replicate_finish_number:nw {#1}
        \q_recursion_tail \q_recursion_stop
      }
    \__perror_replicate_if_digit:NTF #2
      { \__perror_replicate_collect_number:nw { #1 #2 } }
      { \__perror_replicate_finish_number:nw {#1} #2 }
  }
\cs_new:Npn \__perror_replicate_finish_number:nw #1 #2 \q_recursion_stop #3
  {
    \exp_args:Ne \__perror_replicate_output:nw
      { \prg_replicate:nn {#1} {#3} }
    #2 \q_recursion_stop {#3}
  }
\ExplSyntaxOff

\begin{document}
\Replicate{3(2)2(1)}

\Replicate[Z]{3(2)2(1)}

\Replicate[(Z)]{3(2)2(1)}

\Replicate[Z]{3 \textbf{(2) 2}( 1 )}

\edef\zzzzzzzzzzzz{\Replicate[Z]{3\textbf{(2)2}(1)}}

\texttt{\meaning\zzzzzzzzzzzz}

\end{document}

答案3

使用 LuaLaTeX 非常简单(但是,只适用于 LuaLaTeX):

\documentclass{standalone}
\usepackage{luacode}
\newcommand\myexpand[2]{%
\directlua{local function myexpand(x,s) return (x:gsub("(\csstring\%d+)", function(u) return s:rep(math.floor(u)) end)) end tex.sprint(myexpand(\luastring{#1},\luastring{#2}))}}
\begin{document}
\myexpand{(3)2(2)1}{A}
\end{document}

在此处输入图片描述

答案4

如果你对某种语法错误管理感兴趣——我可能会不是只要用户遵守规则,David Carlisle 的代码就会变得非常短且快;-) — 我可以提供一个相当慢的\romannumeral基于扩展的例程

\expandtoA{⟨sequence of numbers denoted by digits and separated from each other by properly matched not nested parentheses⟩}{⟨tokens in case of error⟩}

它无需任何 TeX 扩展,而是经过两个扩展步骤/两次“命中”后即可给出结果\expandafter

第一个参数中的数字和括号必须是类别代码 12(其他)的明确字符标记。

允许出现匹配的、不嵌套的空括号对。

不允许有空格。

如果第一个参数不符合描述的模式,⟨发生错误时的标记⟩将被交付,而不是“转换”为“A”。
您可以使用⟨发生错误时的标记⟩用于触发 LaTeX 错误消息或其他内容。

肯定还有改进/捷径的空间。

\documentclass{article}

\makeatletter    
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@PassFirstToSecond, 
%%    \UD@stopromannumeral, \UD@CheckWhetherNull, 
%%    \UD@CheckWhetherLeadingTokens, \UD@replicate
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% 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>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific 
%% token-sequence that does neither contain explicit character tokens of 
%% category code 1 or 2 nor contain tokens of category code 6:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%%                              {<a <token sequence> without explicit 
%%                                character tokens of category code
%%                                1 or 2 and without tokens of
%%                                category code 6>}%
%%                              {<internal token-check-macro>}%
%%                              {<tokens to be delivered in case
%%                                <argument which is to be checked> has
%%                                <token sequence> as leading tokens>}%
%%                              {<tokens to be delivered in case 
%%                                <argument which is to be checked>
%%                                does not have <token sequence> as
%%                                leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[3]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral
      \expandafter\UD@secondoftwo\string{\expandafter\UD@@CheckWhetherLeadingTokens#3{\relax}#1#2}{}}{}%
  }%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\expandafter\expandafter\expandafter\UD@stopromannumeral
   \expandafter\expandafter\expandafter}%
  \expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%%                              {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything 
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
  \@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
%------------------------------------------------------------------------------
% \UD@replicate{<number>}{<tokens>}
%------------------------------------------------------------------------------
\newcommand\UD@replicateloop[3]{%
  \if m#3\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
  {\UD@replicateloop{#1}{#2#1}}{\UD@stopromannumeral#2}%
}%
\newcommand\UD@replicate[2]{%
  \romannumeral
  \expandafter\UD@Exchange\expandafter{\romannumeral\number\number#1 000}%
                                      {\UD@replicateloop{#2}{}}\relax
}%
%------------------------------------------------------------------------------
\UD@internaltokencheckdefiner{\UD@CheckLeftParen}{(}%
\UD@internaltokencheckdefiner{\UD@CheckRightParen}{)}%
\UD@internaltokencheckdefiner{\UD@CheckZero}{0}%
\UD@internaltokencheckdefiner{\UD@CheckOne}{1}%
\UD@internaltokencheckdefiner{\UD@CheckTwo}{2}%
\UD@internaltokencheckdefiner{\UD@CheckThree}{3}%
\UD@internaltokencheckdefiner{\UD@CheckFour}{4}%
\UD@internaltokencheckdefiner{\UD@CheckFive}{5}%
\UD@internaltokencheckdefiner{\UD@CheckSix}{6}%
\UD@internaltokencheckdefiner{\UD@CheckSeven}{7}%
\UD@internaltokencheckdefiner{\UD@CheckEight}{8}%
\UD@internaltokencheckdefiner{\UD@CheckNine}{9}%
%------------------------------------------------------------------------------
\newcommand\expandtoA[2]{%
  \romannumeral\expandtoALoop{#1}{#2}{}{}{}%
}%
%------------------------------------------------------------------------------
\newcommand\expandtoALoop[5]{%
  % #1 Remaining argument to examine
  % #2 Tokens in case of error
  % #3 Flag denoting how next number must terminate: 
  %    Empty -> next number must terminate due to opening parenthesis or emptiness of remaining argument to examine.
  %    Not empty -> next number must terminate due to closing parenthesis.
  % #4 Digits of next number gathered so far.
  % #5 Result gathered so far.
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherNull{#3}{%
       \expandafter\expandafter\expandafter\UD@Exchange
       \expandafter\expandafter\expandafter{\UD@replicate{#4}{A}}%
       {\UD@stopromannumeral#5}%
    }{\UD@stopromannumeral#2}%
  }{%
    \UD@CheckWhetherLeadingTokens{#1}{(}{\UD@CheckLeftParen}{%
      \UD@CheckWhetherNull{#3}{%
        \expandafter\UD@PassFirstToSecond\expandafter{%
           \romannumeral
           \expandafter\expandafter\expandafter\UD@Exchange
           \expandafter\expandafter\expandafter{\UD@replicate{#4}{A}}{\UD@stopromannumeral#5}%
        }%
        {\expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{\relax}{}}%
      }{\UD@stopromannumeral#2}%
    }{%
      \UD@CheckWhetherLeadingTokens{#1}{)}{\UD@CheckRightParen}{%
        \UD@CheckWhetherNull{#3}{\UD@stopromannumeral#2}{%
          \expandafter\UD@PassFirstToSecond\expandafter{%
             \romannumeral
             \expandafter\expandafter\expandafter\UD@Exchange
             \expandafter\expandafter\expandafter{\UD@replicate{#4}{A}}{\UD@stopromannumeral#5(})%
          }%
          {\expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{}{}}%
        }%
      }{%
        \UD@CheckWhetherLeadingTokens{#1}{0}{\UD@CheckZero}{%
          \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#40}{#5}%
        }{%
          \UD@CheckWhetherLeadingTokens{#1}{1}{\UD@CheckOne}{%
            \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#41}{#5}%
          }{%
            \UD@CheckWhetherLeadingTokens{#1}{2}{\UD@CheckTwo}{%
              \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#42}{#5}%
            }{%
              \UD@CheckWhetherLeadingTokens{#1}{3}{\UD@CheckThree}{%
                \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#43}{#5}%
              }{%
                \UD@CheckWhetherLeadingTokens{#1}{4}{\UD@CheckFour}{%
                  \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#44}{#5}%
                }{%
                  \UD@CheckWhetherLeadingTokens{#1}{5}{\UD@CheckFive}{%
                    \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#45}{#5}%
                  }{%
                    \UD@CheckWhetherLeadingTokens{#1}{6}{\UD@CheckSix}{%
                      \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#46}{#5}%
                    }{%
                      \UD@CheckWhetherLeadingTokens{#1}{7}{\UD@CheckSeven}{%
                        \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#47}{#5}%
                      }{%
                        \UD@CheckWhetherLeadingTokens{#1}{8}{\UD@CheckEight}{%
                          \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#48}{#5}%
                        }{%
                          \UD@CheckWhetherLeadingTokens{#1}{9}{\UD@CheckNine}{%
                            \expandafter\expandtoALoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}{#49}{#5}%
                          }{%
                            \UD@stopromannumeral#2%
                          }%
                        }%
                      }%
                    }%
                  }%
                }%
              }%
            }%
          }%
        }%
      }%
    }%
  }%
}%
%------------------------------------------------------------------------------
\makeatother

\begin{document}

\message{\detokenize\expandafter\expandafter\expandafter{\expandtoA{1(2)3(4)5(6)7(8)9(10)()(11)}{ErrorText}}}%

\message{\detokenize\expandafter\expandafter\expandafter{\expandtoA{1(2)3(4)5(6)7(89(10)()(11)}{ErrorText}}}%

\end{document}

上述代码传递了消息

A(AA)AAA(AAAA)AAAAA(AAAAAA)AAAAAAA(AAAAAAAA)AAAAAAAAA(AAAAAAAAAA)()(AAAAAAAAAAA)

ErrorText

到终端。(第二次调用时出现 ErrorText,因为这里的括号没有正确匹配。)

相关内容