我编写了一个文档类,并希望根据用户定义的数量执行某些操作。因此,我认为应该使用foreach
PGF 中的循环以及访问器宏:
\documentclass{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox,pgffor}
\makeatletter
\csdef{my@test}{3}
\DeclareDocumentCommand{\val}{m g}{%
\csname #1@#2\endcsname%
}
\makeatother
\begin{document}
Testing direct: \csname my@test\endcsname
Testing macro: \val{my}{test}
\foreach \i in {1,...,\csname my@test\endcsname}{a}
\foreach \i in {1,...,\val{my}{test}}{a}
\end{document}
不幸的是,使用val
break foreach
:
出现错误消息Illegal unit of measure (pt inserted)
(多次)。
有趣的是:如果我使用参数规范{m m}
而不是{m g}
,问题就会消失。
在类定义中不使用宏是可行的——但是我确实需要可选参数——但是这里发生了什么?
背景
X
用户可以使用以下形式的宏来创建 s 列表
\addX{group}{name}{key1=val1,key2=val2,..}
xparse
此宏由和实现keyval
。它将信息存储在 形式的宏中\X@group@name@key
。为了向用户隐藏此信息,我提供了类似val
上述的访问器宏。还有一些宏和环境直接利用创建的列表,其中一个将包含如上定义的循环,X
根据用户提供的值重复执行(针对每个 )操作。
的最后一个参数val
是可选的,这样如果用户不提供键(我认为这将是最常请求的值),则可以返回默认值。
答案1
这似乎是一个可扩展性问题。似乎xparse
定义带有可选参数的函数的方式阻止了\foreach
在其域中看到最终值。特别是,\foreach
无法完全扩展宏的值,因为xparse
创建g
类型参数的方法阻止了完全扩展。
即使可选参数位于强制参数之前,您也会遇到类似的错误:
\documentclass{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox,pgffor}
\makeatletter
\csdef{my@test}{3}
\DeclareDocumentCommand{\val}{o m}{%
\csname #1@#2\endcsname%
}
\makeatother
\begin{document}
Testing direct: \csname my@test\endcsname
Testing macro: \val[my]{test}
\foreach \i in {1,...,\csname my@test\endcsname}{a}
\foreach \i in {1,...,\val[my]{test} }{a}
\end{document}
在文档中,xparse
您可以创建具有可选参数的完全可扩展函数,但存在各种限制:
可选参数可能不是以下类型
g
最后一个参数可能不是可选参数。
您可以编写以下内容
\documentclass{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox,pgffor}
\makeatletter
\csdef{my@test}{3}
\DeclareExpandableDocumentCommand{\val}{o m}{%
\csname #1@#2\endcsname%
}
\makeatother
\begin{document}
Testing direct: \csname my@test\endcsname
Testing macro: \val[my]{test}
\foreach \i in {1,...,\csname my@test\endcsname}{a}
\foreach \i in {1,...,\val[my]{test} }{a}
\end{document}
通过一些小技巧,你可以部分了解扩张方面正在发生的事情
\documentclass{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox,pgffor}
\makeatletter
\csdef{my@test}{3}
%% an expandable version of your macro
\DeclareExpandableDocumentCommand{\eval}{o m}{%
\csname #1@#2\endcsname%
}
%% a non-expandable version of your macro
\DeclareDocumentCommand{\val}{o m}{%
\csname #1@#2\endcsname%
}
\makeatother
\setlength{\parskip}{2ex}
\begin{document}
\makebox[1in][r]{Full expanded}:
\begin{minipage}[t]{3in}\ttfamily
\detokenize\expandafter{\romannumeral-`G\eval[my]{test}}
\end{minipage}
\makebox[1in][r]{Expanded once}:
\begin{minipage}[t]{3in}\ttfamily
\detokenize\expandafter{\eval[my]{test}}
\end{minipage}
\makebox[1in][r]{Not fully expandable}:
\begin{minipage}[t]{3in}\ttfamily
\detokenize\expandafter{\romannumeral-`G\val[my]{test}}
\end{minipage}
\end{document}
也许如果您阐明这个宏如何运行,我们就能想出一个更能满足您需求的解决方案。
更新
您可以使两个参数都成为必需的,但要测试第二个参数是否传递了空参数。
代码如下:
\csdef{my@default}{5}
\DeclareDocumentCommand{\eval}{ mm }{%%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax
\csname #1@default\endcsname
\else
\csname #1@#2\endcsname
\fi
}
然后你可以写类似
\foreach \i in {1,...,\eval{my}{}}{D}
\foreach \i in {1,...,\eval{my}{test}}{c}
不过我应该指出,\eval{my}{ }
对于一个空洞的论点来说,这是不正确的。