替换顶层(即未括在括号中)出现的标记

替换顶层(即未括在括号中)出现的标记

我正在尝试用令牌列表中的另一个令牌替换所有顶级令牌。例如,01in替换0{0{0}}0应该会产生1{0{0}}1。我有一个可以工作的东西,但一路上忘记了一些括号:它返回10{0}1。什么是最简单(合理有效)的方法来做到这一点?

我可以看到实现此目的的一些不优雅且缓慢的方法(以及一些稍微不准确的方法,例如测试是否\tmvip_output_tl包含多个标记\tl_if_single_token:VTF \tmvip_output_tl而不是测试它是否被括号括起来),但应该有一种优雅且合理有效的方法。

\documentclass{article}
\usepackage{xparse}
\usepackage{stix}

\ExplSyntaxOn

\cs_generate_variant:Nn \tl_if_single_token:nTF {VTF}
\cs_generate_variant:Nn \tl_put_right:Nn {Nf}


\tl_new:N \tmvip_output_tl
\cs_new:Npn \tl_map_variable_in_place:NNn #1 #2 #3 {
  \tl_set:Nn \tmvip_output_tl {}
  \tl_map_variable:NNn #1 #2 {
    #3
    \tl_put_right:NV \tmvip_output_tl #2 % TODO: Make this add braces if needed
  }
  \tl_set_eq:NN #1 \tmvip_output_tl
}

\tl_new:N \ratl_item_tl
\tl_new:N \ratl_old_tl
\tl_new:N \raft_new_tl
\cs_new:Npn \tl_replace_all_top_level:Nnn #1 #2 #3 {
  \tl_set:Nn \ratl_old_tl {#2}
  \tl_set:Nn \ratl_new_tl {#3}
  \tl_map_variable_in_place:NNn #1 \ratl_item_tl {
    \tl_if_eq:NNTF \ratl_item_tl \ratl_old_tl {
      \tl_gset_eq:NN \ratl_item_tl \ratl_new_tl
    } {}
  }
}

\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn

\tl_new:N \input_tl
\tl_set:Nn \input_tl {0{0{0}}0}

\tl_new:N \output_tl
\tl_set_eq:NN \output_tl \input_tl
\tl_replace_all_top_level:Nnn \output_tl {0} {1}

\tl_new:N \expected_output_tl
\tl_set:Nn \expected_output_tl {1{0{0}}1}

\newcommand{\inputtl}{\tl_to_str:N \input_tl}
\newcommand{\outputtl}{\tl_to_str:N \output_tl}
\newcommand{\expectedoutputtl}{\tl_to_str:N \expected_output_tl}

\ExplSyntaxOff

I want to replace all occurrences of 0 that are at the top-level (i.e. not in a group) by 1. It works, but loses the braces in the process: "\inputtl" becomes "\outputtl" instead of "\expectedoutputtl"

\end{document}

答案1

您可能没有意识到,因为 interface3.pdf 中似乎没有提到,而\tl_replace_all:Nnn只是替换了您所说的“顶层发生”。
因此,如果使用 expl3,那么 - 在我看来 - 最简单(合理有效)的方法是使用\tl_replace_all:Nnn。 :-)

\documentclass{article}

\begin{document}

\ExplSyntaxOn
\tl_set:Nn \output_tl {0{0{0}}0}
\tl_replace_all:Nnn \output_tl {0} {1}
\tl_show:N \output_tl

\tl_set:Nn \output_tl {~0~{~0~{~0~}~}~~0~}
\tl_replace_all:Nnn \output_tl {0} {1}
\tl_show:N \output_tl
\ExplSyntaxOff

\end{document}

终端输出:

> \output_tl=1{0{0}}1.
<recently read> }
                 
l.8 \tl_show:N \output_tl
                         
? 
> \output_tl= 1 { 0 { 0 } } 1 .
<recently read> }
                 
l.12 \tl_show:N \output_tl

您的方法存在的问题是:

内部\tl_map_variable:NNn使用。

Interface3.pdf 说:

\tl_map_variable:NNn ⟨tl var⟩ ⟨variable⟩ {⟨code⟩}

将 ⟨tl var⟩ 中的每个 ⟨item⟩ 依次存储在(标记列表) ⟨variable⟩ 中,并应用 ⟨code⟩。⟨code⟩ 通常会使用 ⟨variable⟩,但这不是强制的。对 ⟨variable⟩ 的赋值是局部的。循环后的值为 ⟨tl var⟩ 中的最后一个 ⟨item⟩,如果 ⟨tl var⟩ 为空,则为原始值。另请参阅\tl_map_inline:Nn

当将 ⟨tl var⟩ 中的一项映射到 ⟨variable⟩ 时,\tl_map_variable:NNn如果存在大括号,则会剥去围绕整个项的一层大括号,但不会提供有关是否发生大括号剥除的信息。
因此,⟨code⟩ 不能根据此类大括号的存在进行分叉。除此之外,\tl_map_variable:NNn⟨tl var⟩ 项之间的空格标记将被丢弃。


如果您出于其他目的/在其他上下文中需要它,则\MyModule_CheckWhetherExplicitBrace:nnn可以使用以下内容来测试宏参数的第一个标记是否是类别 1(开始组)的显式字符标记。
通常{是唯一分配了类别代码 1(开始组)的字符。
如果参数具有第一个标记,则测试会产生真正的分支。 如果参数具有第一个标记,则测试也会产生真正的分支,例如 。 (您可以通过在或相应的 expl3 指令生效时对事物进行标记来获得。)控制字 之后,标记不是类别 1(开始组)的显式字符标记,而是隐式字符标记。因此,即使在这种情况下,如果参数具有第一个标记,则测试也会产生错误分支。{1
K1
K1\catcode`\K=1\relax\char_set_catcode_group_begin:N \K
\let\bgroup={\bgroup\bgroup

\ExplSyntaxOn

%% Some framework for displaying the test result on terminal:

\msg_new:nnnn {MyModule}
              {Argument does indeed have a first token which is an explicit character token of category 1}
              {The\ argument\ "#1"\ does\ have\ a\ first\ token\ which\ is\ an\ explicit\ character-token\ of\ category\ 1\ (begin\ group).}
              {This\ message\ is\ just\ an\ information.}
\msg_new:nnnn {MyModule}
              {Argument does not have a first token which is an explicit character token of category 1}
              {The\ argument\ "#1"\ does\ NOT\ have\ a\ first\ token\ which\ is\ an\ explicit\ character-token\ of\ category\ 1\ (begin\ group).}
              {This\ message\ is\ just\ an\ information.}

\cs_new:Nn \MyModule_DisplayBraceCheckResult:n {
 \msg_term:nnn {MyModule}
               {Argument does \MyModule_CheckWhetherExplicitBrace:nnn{#1}{indeed}{not} have a first token which is an explicit character token of category 1}
               {#1}
}

%% The test:

%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit character token of
%% category 1 (begin group)
%%.............................................................................
%% \MyModule_CheckWhetherExplicitBrace:nnn{<Argument which is to be checked>}%
%%                                        {<Tokens to be delivered in case that argument
%%                                          which is to be checked has a leading
%%                                          explicit catcode-1-character-token>}%
%%                                        {<Tokens to be delivered in case that argument
%%                                          which is to be checked does not have a
%%                                          leading explicit catcode-1-character-token>}%
\cs_new:Npn \MyModule_CheckWhetherExplicitBrace:nnn #1 {
  \exp:w\exp_after:wN\use_ii:nn\exp_after:wN{\exp_after:wN{\token_to_str:N #1.}
  \exp_after:wN\use_i:nn\exp_after:wN{\exp_after:wN\use_ii:nn \token_to_str:N}
  \exp_after:wN\exp_end:\use_i:nn}{\exp_after:wN\exp_end:\use_ii:nn}
}

\MyModule_DisplayBraceCheckResult:n {ABC}

\MyModule_DisplayBraceCheckResult:n {}

\MyModule_DisplayBraceCheckResult:n {~}

\MyModule_DisplayBraceCheckResult:n {~{A}BC}

\MyModule_DisplayBraceCheckResult:n {{A}BC}

\MyModule_DisplayBraceCheckResult:n {{ABC}}

\MyModule_DisplayBraceCheckResult:n {{}ABC}

\MyModule_DisplayBraceCheckResult:n {{{A}B}C}

\stop

终端和.log文件中的输出:

The argument "ABC" does NOT have a first token which is an explicit
character-token of category 1 (begin group).
The argument "" does NOT have a first token which is an explicit
character-token of category 1 (begin group).
The argument " " does NOT have a first token which is an explicit
character-token of category 1 (begin group).
The argument " {A}BC" does NOT have a first token which is an explicit
character-token of category 1 (begin group).
The argument "{A}BC" does have a first token which is an explicit
character-token of category 1 (begin group).
The argument "{ABC}" does have a first token which is an explicit
character-token of category 1 (begin group).
The argument "{}ABC" does have a first token which is an explicit
character-token of category 1 (begin group).
The argument "{{A}B}C" does have a first token which is an explicit
character-token of category 1 (begin group).

如果你想检测一个参数——让我们通过表示该参数#1——是否形成一组完全嵌套在花括号之间的标记,你可以申请\MyModule_CheckWhetherExplicitBrace:nnn {#1}{...}{...}检查是否#1有第一个标记,它是类别 1(开始组)的显式字符标记,如果是,检查“吞噬”/从中删除未限定的参数是否#1会产生空值,可能通过某种变体\tl_if_empty:nTF :

\ExplSyntaxOn

%% Some framework for displaying the test result on terminal:

\msg_new:nnnn {MyModule}
              {Argument does indeed consist of a set of token which is nested in curly braces}
              {The\ argument\ "#1"\ consists\ of\ a\ set\ of\ tokens\ which\ is\ nested\ in\ curly\ braces.}
              {This\ message\ is\ just\ an\ information.}
\msg_new:nnnn {MyModule}
              {Argument does not consist of a set of token which is nested in curly braces}
              {The\ argument\ "#1"\ DOES\ NOT\ consist\ of\ a\ set\ of\ tokens\ which\ is\ nested\ in\ curly\ braces.}
              {This\ message\ is\ just\ an\ information.}

\cs_new:Nn \MyModule_DisplayBraceNestedCheckResult:n {
 \msg_term:nnn {MyModule}
               {
                   Argument does
                   \MyModule_CheckWhetherExplicitBrace:nnn{#1}
                                                          {\tl_if_empty:oTF{\use_none:n #1}{indeed}{not}}
                                                          {not}
                   consist of a set of token which is nested in curly braces
               }
               {#1}
}

%% The test:

%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit character token of
%% category 1 (begin group)
%%.............................................................................
%% \MyModule_CheckWhetherExplicitBrace:nnn{<Argument which is to be checked>}%
%%                                        {<Tokens to be delivered in case that argument
%%                                          which is to be checked has a leading
%%                                          explicit catcode-1-character-token>}%
%%                                        {<Tokens to be delivered in case that argument
%%                                          which is to be checked does not have a
%%                                          leading explicit catcode-1-character-token>}%
\cs_new:Npn \MyModule_CheckWhetherExplicitBrace:nnn #1 {
  \exp:w\exp_after:wN\use_ii:nn\exp_after:wN{\exp_after:wN{\token_to_str:N #1.}
  \exp_after:wN\use_i:nn\exp_after:wN{\exp_after:wN\use_ii:nn \token_to_str:N}
  \exp_after:wN\exp_end:\use_i:nn}{\exp_after:wN\exp_end:\use_ii:nn}
}

\MyModule_DisplayBraceNestedCheckResult:n {ABC}

\MyModule_DisplayBraceNestedCheckResult:n {A}

\MyModule_DisplayBraceNestedCheckResult:n {}

\MyModule_DisplayBraceNestedCheckResult:n {~}

\MyModule_DisplayBraceNestedCheckResult:n {~{A}BC}

\MyModule_DisplayBraceNestedCheckResult:n {{}ABC}

\MyModule_DisplayBraceNestedCheckResult:n {{A}BC}

\MyModule_DisplayBraceNestedCheckResult:n {{ABC}}

\MyModule_DisplayBraceNestedCheckResult:n {{{A}BC}}

\MyModule_DisplayBraceNestedCheckResult:n {{A}}

\stop

终端和.log文件中的输出:

The argument "ABC" DOES NOT consist of a set of tokens which is nested in
curly braces.
The argument "A" DOES NOT consist of a set of tokens which is nested in curly
braces.
The argument "" DOES NOT consist of a set of tokens which is nested in curly
braces.
The argument " " DOES NOT consist of a set of tokens which is nested in curly
braces.
The argument " {A}BC" DOES NOT consist of a set of tokens which is nested in
curly braces.
The argument "{}ABC" DOES NOT consist of a set of tokens which is nested in
curly braces.
The argument "{A}BC" DOES NOT consist of a set of tokens which is nested in
curly braces.
The argument "{ABC}" consists of a set of tokens which is nested in curly
braces.
The argument "{{A}BC}" consists of a set of tokens which is nested in curly
braces.
The argument "{A}" consists of a set of tokens which is nested in curly
braces.

答案2

令牌循环非常简单。有了这个定义,组内的任何内容都会直接回显到输出令牌列表中,而无需替换。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\newcommand\reptoplist[3]{%
  \tokcycle
    {\ifx#2##1\addcytoks{#3}\else\addcytoks{##1}\fi}
    {\addcytoks{##1}}
    {\addcytoks{##1}}
    {\addcytoks{##1}}
    {#1}%
    \the\cytoks
}
\begin{document}
\reptoplist{0{0{0}}0}{0}{1}

\detokenize\expandafter{\the\cytoks}
\end{document}

在此处输入图片描述

ps 即使输入包含空格和/或控制序列,这种方法也能奏效,例如\reptoplist{0 {0{0}}\today}{0}{1}。当然,对于控制序列,替换发生在控制序列扩展之前……但是,如果需要,也可以更改,例如:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\newcommand\reptoplist[3]{%
  \expandedtokcycle
    {\ifx#2##1\addcytoks{#3}\else\addcytoks{##1}\fi}
    {\addcytoks{##1}}
    {\addcytoks{##1}}
    {\addcytoks{##1}}
    {#1}%
    \the\cytoks
}
\begin{document}
\reptoplist{0 {0{0}}\today}{2}{3}

\detokenize\expandafter{\the\cytoks}
\end{document}

在此处输入图片描述

相关内容