我正在尝试创建自己的模板包。
在此包中,定义了几个自定义环境。其中一个环境开始和结束一个表,即:
\newenvironment{customenvtab}{\begin{tabular{c|c}}{\end{tabular}}
在这个环境中,我定义了一个函数来填充表格。该函数使用在 pgfkeys 包的帮助下定义的宏来使用键值参数。问题是,只有第一个键值参数在使用时映射正确。当我删除表格环境时,两个参数在使用时都映射正确。
我究竟做错了什么?
仅供参考:我不太精通 LaTeX。
在此先非常感谢您的回复(如果有遗漏之处,还希望您能耐心等待)。
这是我的最小示例:
\documentclass{article}
\usepackage{pgfkeys}
\def\customvarset{\pgfqkeys{/rpath}}
\newenvironment{customenvnotab}{%
\newcommand*{\resetvarset}{%
\customvarset{%
spath/.cd,%
var1/.initial=var1 tbd,%
var2/.initial=var2 tbd
}
}
\resetvarset
\def\notabval##1{\pgfkeysvalueof{/rpath/spath/##1}}
\newcommand{\cmdnotab}[1]{%
\customvarset{spath/.cd, ##1}
\notabval{var1}, \notabval{var2}
\resetvarset
}
}{}
\newenvironment{customenvtab}{%
\newcommand*{\resetvarsettab}{%
\customvarset{%
tpath/.cd,%
var3/.initial=var3 tbd,%
var4/.initial=var4 tbd
}
}
\resetvarsettab
\def\tabval##1{\pgfkeysvalueof{/rpath/tpath/##1}}
\newcommand{\cmdtab}[1]{%
\customvarset{tpath/.cd, ##1}
\tabval{var3} & \tabval{var4}
\resetvarsettab
}
\begin{tabular}{c|c}
}{%
\end{tabular}
}
\begin{document}
\section{Working}
\begin{customenvnotab}
\cmdnotab{}\\
\cmdnotab{var1=test var1}\\
\cmdnotab{var2=test var2}\\
\cmdnotab{var1=bla, var2=blu}
\end{customenvnotab}
\section{Not Working}
\begin{customenvtab}
\cmdtab{}\\
\cmdtab{var3=test var3}\\
\cmdtab{var4=test var4}\\
\cmdtab{var3=blo, var4=bli}
\end{customenvtab}
\end{document}
答案1
问题在于该\pgfqkeys
命令执行的是局部赋值,而每个表格单元格(TeXbook 术语中的“对齐条目”)都隐式地形成一个组。因此,当您的\cmdtab
宏输出时&
,前一列中执行的局部赋值会被遗忘,这解释了为什么您在第二列中没有得到预期的结果。
因此,最简单的补救措施是在每个单元格中执行分配,例如通过修改宏的定义,\cmdtab
如下所示:
\newcommand*{\cmdtab}[1]{%
\customvarset{tpath/.cd, ##1}\tabval{var3}%
&\customvarset{tpath/.cd, ##1}\tabval{var4}%
\resetvarsettab
}
然而,这有点浪费和多余(所有键都##1
以完全相同的方式在每个条目中重复分配)。
另一种可能性是,一旦以特殊方式设置, (内部\pgfkeys{...}
)/path/to/key=value
便执行全局分配/path/to/key
。为此:
我定义了一个名为的处理程序
.my gset
,以便/path/to/key/.my gset=value
在全局上存储在用于直接包含值的键的value
同一个宏中(为了避免“保存堆栈累积”,您不应该对使用处理程序的键执行任何本地分配 - 按照通常的规则:不要将本地和全局分配混合到同一个宏或 TeX 寄存器)。pgfkeys
.my gset
为了您的方便,我提供了一个额外的处理程序:
.my set up keys for global assignment
。之后/some/path/.my set up keys for global assignment={key1, ..., keyn}
,执行/some/path/keyi=value
一些我在 {1, ...,n} 执行/some/path/keyi/.my gset=value
(\pgfkeys{...}
当然,所有这些都在 内部)。换句话说,这将设置命名键,以便对它们的直接赋值是全局的。
关于您的代码:我避免在环境定义中嵌套宏定义,因为这很浪费并且使代码的可读性降低(环境逻辑混乱,需要加倍井号等)。
\documentclass{article}
\usepackage{pgfkeys}
\usepackage{pgffor}
\usepackage{etoolbox}
\makeatletter
\pgfkeysdef{/handlers/.my gset}
{%
\csgdef{pgfk@\pgfkeyscurrentpath}{#1}%
}
% Set up key \pgfkeyscurrentpath/#1 (let's call it 'foo') so that
% \pgfkeys{foo=value} performs \pgfkeys{foo/.my gset=value}.
\pgfkeysdef{/handlers/.my set up key for global assignment}
{%
\begingroup
\edef\tmp{\endgroup
\noexpand\pgfkeys{%
\pgfkeyscurrentpath/#1/.code={\noexpand\pgfkeys{%
\pgfkeyscurrentpath/#1/.my gset=####1}}%
}%
}\tmp
}
% Call the '.my set up key for global assignment' handler for every key in comma
% list #1.
\pgfkeysdef{/handlers/.my set up keys for global assignment}
{%
\pgfkeys{%
\pgfkeyscurrentpath/.my set up key for global assignment/.list={#1}}%
}
\newcommand*{\customvarset}{\pgfqkeys{/rpath}}
\newcommand*{\customenv@resetvarsettab}{%
\customvarset{
tpath/.cd,
.my set up keys for global assignment={var3, var4},
var3=var3 tbd,
var4=var4 tbd,
}%
}
\newcommand*{\tabval}[1]{\pgfkeysvalueof{/rpath/tpath/#1}}
\newcommand*{\customenv@cmdtab}[1]{%
\customvarset{tpath/.cd, #1}%
\tabval{var3}&\tabval{var4}%
\customenv@resetvarsettab
}%
\newenvironment{customenvtab}{%
\customenv@resetvarsettab
\let\cmdtab\customenv@cmdtab
\begin{tabular}{c|c}
}{%
\end{tabular}%
}
\makeatother
\begin{document}
\begin{customenvtab}
\cmdtab{}\\
\cmdtab{var3=test var3}\\
\cmdtab{var4=test var4}\\
\cmdtab{var3=blo, var4=bli}
\end{customenvtab}
\end{document}
答案2
使用 几乎是微不足道的expkv-cs
,因为expkv-cs
它不能通过分配来工作,而只能通过参数转发来工作(因此在组末尾不会丢失任何分配)。
下面使用了稍微复杂一些(但更容易扩展)的哈希变体expkv-cs
。我还将所有键放在一个键=值集(或术语中的路径pgfkeys
)中。下面的另一个示例使用更简单的拆分机制和分离集。
使用\ekvcHashAndUse
在哈希变体中,您可以使用键名访问键的值\ekvcValue
(第二个参数是宏预处理的键列表,\cmdkeys
以便\ekvcValue
可以快速获取正确的值 - 该\ekvcHashAndUse
版本将在键=值列表之后调用宏,并使用处理后的列表作为参数)。
\documentclass{article}
\usepackage{expkv-cs}
\ekvcHashAndUse\cmdkeys
{
var1 = var1 tbd
,var2 = var2 tbd
,var3 = var3 tbd
,var4 = var4 tbd
}
\newenvironment{customenvnotab}
{%
\newcommand\cmdnotab[1]{\cmdkeys{##1}\cmdnotabDo}%
\newcommand\cmdnotabDo[1]{\ekvcValue{var1}{##1}, \ekvcValue{var2}{##1}}%
}
{}
\newenvironment{customenvtab}
{%
\newcommand\cmdtab[1]{\cmdkeys{##1}\cmdtabDo}%
\newcommand\cmdtabDo[1]{\ekvcValue{var3}{##1}&\ekvcValue{var4}{##1}}%
\begin{tabular}{c|c}%
}
{%
\end{tabular}%
}
\begin{document}
\section{Working}
\begin{customenvnotab}
\cmdnotab{}\\
\cmdnotab{var1=test var1}\\
\cmdnotab{var2=test var2}\\
\cmdnotab{var1=bla, var2=blu}
\end{customenvnotab}
\section{Also Working}
\begin{customenvtab}
\cmdtab{}\\
\cmdtab{var3=test var3}\\
\cmdtab{var4=test var4}\\
\cmdtab{var3=blo, var4=bli}
\end{customenvtab}
\end{document}
使用\ekvcSplit
在拆分变体中,键直接对应于参数(仅支持 9 个不同的主键\ekvcSplit
,如果您需要更多,则需要使用\ekvcSplitAndForward
或\ekvcSplitAndUse
,并且事情很快就会变得非常复杂)。
\documentclass{article}
\usepackage{expkv-cs}
\ekvcHashAndUse\cmdkeys
{
var1 = var1 tbd
,var2 = var2 tbd
,var3 = var3 tbd
,var4 = var4 tbd
}
\newenvironment{customenvnotab}
{\ekvcSplit\cmdnotab{var1=var1 tbd,var2=var2 tbd}{##1, ##2}}
{}
\newenvironment{customenvtab}
{%
\ekvcSplit\cmdtab{var3=var3 tbd,var4=var4 tbd}{##1&##2}%
\begin{tabular}{c|c}%
}
{%
\end{tabular}%
}
\begin{document}
\section{Working}
\begin{customenvnotab}
\cmdnotab{}\\
\cmdnotab{var1=test var1}\\
\cmdnotab{var2=test var2}\\
\cmdnotab{var1=bla, var2=blu}
\end{customenvnotab}
\section{Also Working}
\begin{customenvtab}
\cmdtab{}\\
\cmdtab{var3=test var3}\\
\cmdtab{var4=test var4}\\
\cmdtab{var3=blo, var4=bli}
\end{customenvtab}
\end{document}
两者的输出
答案3
另一种解决方案是使用pgfkeys
。
这定义了两个辅助宏\addtomacro
(从 复制定义expl3
)和\addpgfkeysvaluetomacro
,它们从 pgf-key 中检索值并将结果附加到现有宏定义的右侧。
tabular
这样,您可以在组(如单元格)末尾恢复所有键值的本地分配之前检索它们。
\documentclass{article}
\usepackage{pgfkeys}
\def\customvarset{\pgfqkeys{/rpath}}
\newenvironment{customenvnotab}{%
\newcommand*{\resetvarset}{%
\customvarset{%
spath/.cd,%
var1/.initial=var1 tbd,%
var2/.initial=var2 tbd
}%
}%
\resetvarset
\def\notabval##1{\pgfkeysvalueof{/rpath/spath/##1}}%
\newcommand{\cmdnotab}[1]{%
\customvarset{spath/.cd, ##1}%
\notabval{var1}, \notabval{var2}%
\resetvarset
}%
}{}
\ExplSyntaxOn
\cs_new_eq:NN \addtomacro \tl_put_right:Nn
\ExplSyntaxOff
\newcommand\addpgfkeysvaluetomacro[2]
{%
\begingroup
\pgfkeysgetvalue{#2}\tmp
\expandafter
\endgroup
\expandafter\addtomacro\expandafter#1\expandafter{\tmp}%
}
\newenvironment{customenvtab}{%
\newcommand*{\resetvarsettab}{%
\customvarset{%
tpath/.cd,%
var3/.initial=var3 tbd,%
var4/.initial=var4 tbd
}%
}%
\resetvarsettab
\def\tabval##1{\pgfkeysvalueof{/rpath/tpath/##1}}%
\newcommand{\cmdtab}[1]{%
\begingroup
\customvarset{tpath/.cd, ##1}%
\pgfkeysgetvalue{/rpath/tpath/var3}\tmp
\addtomacro\tmp{&}%
\addpgfkeysvaluetomacro\tmp{/rpath/tpath/var4}%
\expandafter
\endgroup
\tmp
}%
\begin{tabular}{c|c}
}{%
\end{tabular}%
}
\begin{document}
\section{Working}
\begin{customenvnotab}
\cmdnotab{}\\
\cmdnotab{var1=test var1}\\
\cmdnotab{var2=test var2}\\
\cmdnotab{var1=bla, var2=blu}
\end{customenvnotab}
\section{Not Working}
\begin{customenvtab}
\cmdtab{}\\
\cmdtab{var3=test var3}\\
\cmdtab{var4=test var4}\\
\cmdtab{var3=blo, var4=bli}
\end{customenvtab}
\end{document}