在 \edef 中定义

在 \edef 中定义
The following throws an "Undefined control sequence" error:

\edef\testa{\edef\testb{foobar}\testb}

当涉及到扩展 \testb 时。这个问题在“TEX by Topic”12.6.2 中也提到过。但没有给出解决方案。

EDIT2,这是一个最小的不起作用的例子:

\documentclass{article}

\newenvironment{definition}[1]{\def\currentdef{#1}}{}

\makeatletter

\newcommand\contains[2]{%
    \edef\listarg{#1}%
    \xdef\needle{#2}%
    \expandafter\contains@sub\listarg,\relax\noexpand\@eolst%
}
\def\contains@sub#1,#2\@eolst{%
    \edef\match{#1}%
    \ifx\needle\match%
        true
    \else
        \ifx\relax#2\relax\else
            \contains@sub#2\@eolst%
        \fi
    \fi}

\newcommand\cond[1]{%
    \edef\test{\contains{\currentdef}{#1}}%
    \ifx\relax\test\relax\def\ommitthis\relax\fi%
    \@ifundefined{ommitthis}{#1}{}%
}

\makeatother

\def\test{test2}

\begin{document}

\begin{definition}{test2,test3}
    \cond{test3}
    \cond{\test}
    \cond{test1}
\end{definition}

\end{document}

\cond应检查给定的参数是否在周围定义中列出。因此必须省略 test2 和 test3,只应打印 test1。

答案1

您只需要循环遍历作为参数给出的列表:

\documentclass{article}

\newenvironment{definition}[1]
  {\def\currentdef{#1}}
  {}

\makeatletter
\def\cond#1{\edef\@tempa{#1}%
  \@tempswafalse
  \@for\@tempb:=\currentdef\do
    {\ifx\@tempa\@tempb\@tempswatrue\fi}%
  \if@tempswa\else\@tempa\fi
}
\makeatother

\begin{document}

\def\test{test2}

\begin{definition}{test2,test3}
    \cond{test3}
    \cond{\test}
    \cond{test1}
\end{definition}

\end{document}

环境definition将参数存储在宏中\currentdef(正如您所做的那样)。宏\cond完全扩展其参数并将结果存储在中\@tempa。然后将暂存条件设置为 false,并在与 的每个部分进行比较\@tempswa的帮助下;块\@for\@tempa\currentdef

\@for\@tempb=\LIST\do{<code>}

其中\LIST扩展为a,b,c(任何以逗号分隔的标记列表)执行扩展为<code>,并依次执行。\@tempbabc

<code>在我们的例子中, 是“检查和是否具有\@tempa相同\@tempb的含义(此处为扩展),如果是,则设置\if@tempswa为 true。

最后\cond测试\@iftempswa:如果为真,则不执行其他任何操作,否则\@tempa进行扩展。


可以获得相同的效果,但还可以获得一些改进expl3

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{definition}{m}
 { \canaaerus_store:n { #1 } } { }
\NewDocumentCommand{\cond}{m}
 { \canaaerus_check:x { #1 } }

\cs_new_protected:Npn \canaaerus_store:n #1
 {
  \seq_set_split:Nnn \l_canaaerus_list_seq { , } { #1 }
 }

\cs_new_protected:Npn \canaaerus_check:n #1
 {
  \seq_if_in:NnF \l_canaaerus_list_seq { #1 } { #1 }
 }
\cs_generate_variant:Nn \canaaerus_check:n { x }

\seq_new:N \l_canaaerus_list_seq
\ExplSyntaxOff

改进之处在于列表周围的空格被删除,因此

\begin{definition}{test2 , test3 }

和没有空格的输入一样好。

策略相同,但参数 todefinition存储在序列中(作为奖励,列表参数周围的空格被删除)。循环的工作方式与以前类似:我们检查参数 to \canaaerus_check:x(完全展开)是否出现在序列中;如果没有,则输出该参数。

注意我们如何得到完整的展开:定义的宏是\canaaerus_check:n,但是我们还创建了一个变体,以便使用

\canaaerus_check:x {<tokens>}

因此几乎相当于说

\edef\@tempa{<tokens>}\expandafter\canaaerus_check:n\expandafter{\@tempa}

(我为 LaTeX3 与原始语法的糟糕混合道歉,但这只是举例而已。)“变体”方法的便利性应该是不言而喻的。

相关内容