如何在具有表格的自定义环境中使用 pgfkeys 创建使用多个参数的命令?

如何在具有表格的自定义环境中使用 pgfkeys 创建使用多个参数的命令?

我正在尝试创建自己的模板包。

在此包中,定义了几个自定义环境。其中一个环境开始和结束一个表,即:

\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}

相关内容