我有一个包含循环的宏\@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
。这是保持可扩展性所必需的。