使用 \xappto、\noexpand 和(例如)\emph

使用 \xappto、\noexpand 和(例如)\emph

我想从宏中积累一些文本并将其放置在多部分 Ti 中Z 节点。工作正常,直到文本中出现宏,此时 TeX 失败。如前所述,可以\noexpand在文本中添加宏,但由于可能有很多文本包含不太理想的宏。我查看了评论中的一些指针\unexpanded 因未定义的 LaTeX 符号而失败?,但目前还没有找到解决方法。

\documentclass{article}

\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}

\usetikzlibrary{shapes.multipart}

\NewDocumentCommand{\foo}{m}{%
    \foreach \m [count=\sn from 1] in {#1}{%
        \xappto{\mtext}{\noexpand\nodepart{\numword{\sn}} \m}
    }%
}

\def\numword#1{%
    \ifcase#1 \or one\or two\or three\or four\or five\or six\or seven\or eight\or nine\or ten\or eleven\or twelve\or thirteen\or fourteen \or fifteen\or sixteen\or seventeen\or eightteen\or nineteen\or twenty\fi
}

%% |=====8><-----| %%

\parindent0pt

\begin{document}

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{1,2,3,4}\mtext}; %% <<-- no error
        %{\foo{\emph{1},2,3,4}\mtext}; %% <<-- error
        %{\foo{\noexpand\emph{1},2,3,4}\mtext}; <<-- no error
\end{tikzpicture}

\end{document}

答案1

列表中的每个项目都存储在 中\m,因此您需要扩展\m一次,但不要超过一次,以避免\emph爆炸等情况。一次点击\expandafter将扩展某物一次,并\unexpanded阻止该物扩展,因此如果将两者结合起来,您将获得:

\unexpanded\expandafter{\m}

\expandafter跳过括号并扩展\m)。你不需要\expandafterbefore,\unexpanded因为\unexpanded(就像\toks<num>\detokenize\scantokens等一样)会扩展 之前的所有标记{

此外,还etoolbox提供了一个方便命名的\expandonce宏,它的作用正是如此:

\documentclass{article}

\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}

\usetikzlibrary{shapes.multipart}

\NewDocumentCommand{\foo}{m}{%
    \foreach \m [count=\sn from 1] in {#1}{%
        \xappto{\mtext}{\noexpand\nodepart{\numword{\sn}} \expandonce{\m}}
    }%
}

\def\numword#1{%
    \ifcase#1 \or one\or two\or three\or four\or five\or six\or seven\or eight\or nine\or ten\or eleven\or twelve\or thirteen\or fourteen \or fifteen\or sixteen\or seventeen\or eightteen\or nineteen\or twenty\fi
}

%% |=====8><-----| %%

\parindent0pt

\begin{document}

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{\emph{1},2,3,4}\mtext}; %% <<-- error
\end{tikzpicture}

\end{document}

在此处输入图片描述

答案2

强制expl3版本:

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{shapes.multipart}

\ExplSyntaxOn

\NewDocumentCommand{\foo}{m}
 {
  \seq_set_from_clist:Nn \l_tmpa_seq { #1 }
  \tl_clear:N \l_tmpa_tl
  \seq_map_indexed_inline:Nn \l_tmpa_seq
   {
    \tl_put_right:Nx \l_tmpa_tl { \exp_not:N \nodepart{\numword{##1}}~\exp_not:n { ##2 } }
   }
  \tl_use:N \l_tmpa_tl
}

\ExplSyntaxOff

\newcommand\numword[1]{%
  \ifcase#1 \or one\or two\or three\or four\or five\or six\or
  seven\or eight\or nine\or ten\or eleven\or twelve\or thirteen\or
  fourteen\or fifteen\or sixteen\or seventeen\or eightteen\or
  nineteen\or twenty\fi
}

\begin{document}

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{1,2,3,4}};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{\emph{1},2,3,4}};
\end{tikzpicture}

\end{document}

在此处输入图片描述

列表被转换为序列,因此我们可以\seq_map_indexed_inline:Nn在循环代码中使用 where 来#1引用索引,并#2引用项目。由于我们处于定义中,因此它们变为##1##2

这将填充一个令牌列表变量,我们只需扩展它\numword,就可以立即交付。

一个“更清洁”的版本,无需通过使用间接来抑制扩展。

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{shapes.multipart}

\ExplSyntaxOn

\NewDocumentCommand{\foo}{m}
 {
  \sgmoye_foo:n { #1 }
 }

\seq_new:N \l__sgmoye_foo_items_seq
\tl_new:N \l__sgmoye_foo_body_tl

\cs_new_protected:Nn \sgmoye_foo:n
 {
  \seq_set_from_clist:Nn \l__sgmoye_foo_items_seq { #1 }
  \tl_clear:N \l__sgmoye_foo_body_tl
  \seq_map_indexed_function:NN \l__sgmoye_foo_items_seq \sgmoye_foo_add:nn
  \tl_use:N \l__sgmoye_foo_body_tl
}

\cs_new_protected:Nn \sgmoye_foo_add:nn
 {
  \__sgmoye_foo_add:en { \numword{#1} } { #2 }
 }
\cs_new_protected:Nn \__sgmoye_foo_add:nn
 {
  \tl_put_right:Nn \l__sgmoye_foo_body_tl { \nodepart{#1} #2 }
 }
\cs_generate_variant:Nn \__sgmoye_foo_add:nn { e }

\ExplSyntaxOff

\newcommand\numword[1]{%
  \ifcase#1 \or one\or two\or three\or four\or five\or six\or
  seven\or eight\or nine\or ten\or eleven\or twelve\or thirteen\or
  fourteen\or fifteen\or sixteen\or seventeen\or eightteen\or
  nineteen\or twenty\fi
}

\begin{document}

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{1,2,3,4}};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}
    \node[draw,rectangle split,rectangle split parts=20,rectangle split ignore empty parts]
        {\foo{\emph{1},2,3,4}};
\end{tikzpicture}

\end{document}

相关内容