循环宏的扩展

循环宏的扩展

我有一个包含循环的宏\@for,它一直有效,直到我尝试将其输出写入文件。以下文档显示了该问题。

\documentclass{article}
\makeatletter
\newwrite\my@out
\AtBeginDocument{\immediate\openout\my@out my.tmp}
\AtEndDocument{\immediate\closeout\my@out}

\def\plist#1{%
  \@for\tmpkey:=#1\do{blah }
}
\def\mymacro{%
  \plist{a,b,c}% this works, appears in document.
  \immediate\write\my@out{hello}% make sure our file is okay
  \immediate\write\my@out\plist{a,b,c}% does not work!
}
\makeatother
\begin{document}
  \mymacro
\end{document}

当我注释掉最后一行时\mymacro,代码就可以正常工作,并且 pdf 中会出现“blah blah blah”字样。所以逻辑并没有偏离太多。

但是,当我写入外部文件时,我肯定遇到了扩展时序问题。我收到错误消息:

! Missing number, treated as zero.
<to be read again>
                 \def
l.21 \mymacro

如果有必要,我可以使用其他软件包。我使用 LaTeX2e 标准,\@for以尽量减少软件包的依赖项数量。

\expandafter我已经尝试使用s 和进行了很多排列\edef,但我的理解还不够。

答案1

您的问题是\@for不“可扩展”(它包含一个赋值,这总是一个杀手)。该\write操作是一个“扩展上下文”,就像\edef,所以您需要一个完全可扩展的循环。在这里,我从 LaTeX3 中取出一个并将其重新编码为 LaTeX2e:

\documentclass{article}
\makeatletter
\newwrite\my@out
\AtBeginDocument{\immediate\openout\my@out my.tmp}
\AtEndDocument{\immediate\closeout\my@out}
\long\def\clist@map#1%
  {%
    \expandafter\ifx\expandafter\relax\detokenize{#1}\relax
      \expandafter\@gobble
    \else
      \expandafter\clist@map@aux
    \fi
      {#1}%
  }
\long\def\clist@map@aux#1#2%
  {\clist@map@loop#2#1,\q@tail,\q@stop}
\long\def\clist@map@loop#1#2,%
  {%
    \ifnum\pdfstrcmp{\unexpanded{#2}}{\noexpand\q@tail}=\z@
      \expandafter\clist@map@loop@stop
    \fi
    #1{#2}%
    \clist@map@loop#1%
  }
\long\def\clist@map@loop@stop#1\q@stop{}
\def\plist#1{\clist@map{#1}\plistaux}
\def\plistaux#1{blah }
\def\mymacro{%
  \plist{a,b,c}% this works, appears in document.
  \immediate\write\my@out{hello}% make sure our file is okay
  \immediate\write\my@out{\plist{a,b,c}}% does not work!
}
\makeatletter
\makeatother
\begin{document}
  \mymacro
\end{document}

这使用\pdfstrcmp仅适用于 pdfTeX 的 。要将代码与 XeLaTeX 和 LuaLaTeX 一起使用,请加载pdftexcmds包装并\pdf@strcmp代替使用\pdfstrcmp

代码的一般思路是设置一个循环,循环中有一个已知的结束标记,该标记不会出现在列表中:这里的标记是\q@tail\pdfstrcmp基元让我们能够以可扩展的方式使用“文本”搜索找到它。(没有基元也可以做同样的事情,但这很繁琐。)

expl3仅使用提供循环的版本将会读取

\documentclass{article}
\usepackage{expl3}
\makeatletter
\newwrite\my@out
\AtBeginDocument{\immediate\openout\my@out my.tmp}
\AtEndDocument{\immediate\closeout\my@out}
\ExplSyntaxOn
\def\plist#1{\clist_map_function:nN{#1}\plistaux}
\ExplSyntaxOff
\def\plistaux#1{blah }
\def\mymacro{%
  \plist{a,b,c}% this works, appears in document.
  \immediate\write\my@out{hello}% make sure our file is okay
  \immediate\write\my@out{\plist{a,b,c}}% does not work!
}
\makeatletter
\makeatother
\begin{document}
  \mymacro
\end{document}

(完整的expl3解决方案也将使用 LaTeX3 文件机制。)

在这两种情况下,请注意我已定义\plistaux,它预定义了 的操作\plist。这是保持可扩展性所必需的。

相关内容