与宏关联的 Pgfkeys 会导致意外结果

与宏关联的 Pgfkeys 会导致意外结果

考虑以下类:

% CLASS

% Preamble
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesClass{myclass}[2022/10/11]
\LoadClass[varwidth]{standalone}
\makeatletter

% Pgfoptions
\RequirePackage{pgfopts}
\pgfkeys{
    /myclass/.cd,
    optionA/.store in = \myclass@optionA,
    optionA = ,
    optionB/.store in = \myclass@optionB,
    optionB = \myclass@optionA,
}
\ProcessPgfOptions{/myclass}

\newcommand{\setA}[1]{\pgfkeyssetvalue{/myclass/optionA}{#1}\texttt{setA(#1)}\\}
\newcommand{\setB}[1]{\pgfkeyssetvalue{/myclass/optionB}{#1}\texttt{setB(#1)}\\}
\newcommand{\defineA}[1]{\pgfkeys{/myclass/optionA = #1}\texttt{defineA(#1)}\\}
\newcommand{\defineB}[1]{\pgfkeys{/myclass/optionB = #1}\texttt{defineB(#1)}\\}

\newcommand{\printoptions}{\fbox{\texttt{%
    [A = (\pgfkeysvalueof{/myclass/optionA}, \myclass@optionA)]% 
    [B = (\pgfkeysvalueof{/myclass/optionB}, \myclass@optionB)]
}}\\}
\makeatother

在代码中使用:

\documentclass[optionA = A0]{myclass}
\begin{document}
\printoptions % => [A = (, A0)] [B = (, A0)]
\setA{A1}
\printoptions % => [A = (A1, A0)] [B = (, A0)]
\defineA{A2}
\printoptions % => [A = (A1, A2)] [B = (, A2)]
\setB{B1}
\printoptions % => [A = (A1, A2)] [B = (B1, A2)]
\defineB{B2}
\printoptions % => [A = (A1, A2)] [B = (B1, A2)]
\end{document}

有很多事情我不明白:

问题 0:为什么\pgfkeyssetvalue{/myclass/optionX}{#1}\pgfkeys{/myclass/optionX = #1}会导致不同的结果?

问题 1:为什么打印\pgfkeysvalueof{/myclass/optionX}\myclass@optionX会导致不同的结果,而我期望.store in使两者等价?

问题2:为什么defineA(A2)还要改变的值\myclass@optionB?我原本以为optionB = \myclass@optionA在定义期间pgfkeys只是初始化,而不是两者之间的永久链接。

问题 3:有没有办法将和pgfkeys同义词定义为始终对应于相同的值,例如包括通过或的改变?\pgfkeysvalueof{/myclass/optionX}\myclass@optionX\pgfkeyssetvalue{/myclass/optionX}\pgfkeys{/myclass/optionX = XN}

问题 4:如何初始化pgfkeys以便optionB = \myclass@optionA\myclass@optionB使用 的值进行初始化\myclass@optionA(但如果\myclass@optionA稍后更改,则不会改变 给出的值\myclass@optionB),如下所示:

\printoptions % => [A = (, A0)] [B = (, A0)]
\setA{A1}
\printoptions % => [A = (A1, A0)] [B = (, A0)]
\defineA{A2}
\printoptions % => [A = (A1, A2)] [B = (, A0)] % Difference here: B stays the same!!!!
\setB{B1}
\printoptions % => [A = (A1, A2)] [B = (B1, A0)]
\defineB{B2}
\printoptions % => [A = (A1, A2)] [B = (B1, A0)]

pgfkeys更一般地说,我认为我忽略了和关联背后发生的事情的大局macros,因此任何大局解释都将受到赞赏。

答案1

这试图成为“全局”答案,即使这个答案仅关注的一个方面pgfkeys,即如何存储赋予键的值以供以后使用:.store in\pgfkeyssetvalue/ .initial

一般来说pgfkeys有两种方式来存储值:

  1. 将值存储在键路径中
  2. 将值存储在某个宏中

您可以通过使用.initial处理程序定义键(\pgfkeys{/foo/.initial = 1})来实现第一个目标,然后使用\pgfkeysvalueof或访问值\pgfkeysgetvalue。这只是将宏隐藏在一个不明显的名称中并增加了一个间接级别,其优点是使用与设置键相同的名称来获取其值,而不是另一个宏名称。

第二个是.store in设置,所以\pgfkeys{/foo/.store in = \foo}。这样设置,每次设置键时,的定义\foo都会更改为扩展为给定值。优点是您可以更好地控制存储内容的位置,并且更轻松地扩展它。缺点是同一事物的另一个名称。

当您使用时,\pgfkeyssetvalue{/foo}{bar}您仅分配给第一个存储,您不会调用处理程序设置的代码.store in,因此不会对进行分配\foo,而只会对关键路径进行分配/foo

你更喜欢哪一个(以及应该更喜欢哪一个)并不是规定的,pgfkeys而是把选择权交给用户。虽然这个选择本身这不是什么坏事,但有时(比如你的情况)会导致混乱。我建议选择其中一种机制,并在一个项目中坚持使用(这并不意味着同一作者的另一个项目不能使用另一种方法)。

就我个人而言,我更喜欢第二种方法,因为我看不到这种间接级别的收益(以及我自己的 key=value 实现——广告块: expkv-- 不支持其他)。其他(如@Qrrbrbirlbel)更喜欢第一种方法。

相关内容