我正在尝试用令牌列表中的另一个令牌替换所有顶级令牌。例如,0
用1
in替换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}