Excellentpgfkeys
包提供了两种类型的键:可以使用 声明的常规“以值为中心”的键/.initial
,以及使用 声明的“以宏为中心”的键/.store in
。以下是两个键的示例声明:
\newcommand*{\mystoragemacro}{}
\pgfkeys{
my vkey/.initial = <initial value>,
my mkey/.store in = \mystoragemacro,
my mkey = <initial value>}
设置两种类型的键的方法与通常相同:
\pgfkeys{my vkey = <value>, my mkey = <value>}
但是,根据键的类型,检索存储在这些键中的值的代码的工作方式不同。对于.initial
键,使用\pgfkeysvalueof
(例如\pgfkeysvalueof{my vkey}
) 检索值;对于.store in
键,值始终在存储宏中可用 (例如\mystoragemacro
)。
问题:这两种钥匙各有什么优缺点?什么时候该用钥匙.initial
,什么时候该用.store in
钥匙?
作为我的问题的一个具体例子,为什么 Ryan Reich 使用.store in
钥匙他的\myparbox
例子,而不是使用.initial
钥匙?
答案1
我个人的偏好是完全避免使用.store in
钥匙,但以下情况除外:
当用作未使用它的现有系统的接口
.store in
时,需要密钥。pgfkeys
例如,假设你想在键盘处理过程中改变要打印的内容(\ref
如果它指向\label
随后调用的函数),即
\pgfkeys{ref text = xyz}
\label{after key}
\ref{after key}% Prints "xyz"
该材料以某种方式使用宏存储\@currentlabel
,该宏是 LaTeX2e 内核内部宏。因此,您可以通过声明ref text
以下内容来实现此效果:
\pgfkeys{ref text/.store in = \@currentlabel}
除此之外,我认为编造一个个人命名空间、费力地混淆它,最后重复 的操作是浪费精力\pgfkeysvalueof
。一.initial
与此宏一起使用键的缺点:尽管它会扩展为\pgfk@<key>
,但它会在之后扩展二间接层:首先,扩展\pgfkeysvalueof
,其次,扩展结果\csname
,只有在此之后,实际的宏名称才会到达输入流。即,它看起来像
\pgfkeysvalueof{<key>}
-> \csname pgfk@<key>\endcsname
-> \pgfk@<key>
这使得操作该键变得非常尴尬作为宏;例如,如果你想创建一个快捷方式,\let\mymacro<whatever underlies "key">
你不能这样做
% Makes \mymacro = \pgfkeysvalueof
\let\mymacro\pgfkeysvalueof{<key>}
也不你能用通常的(丑陋的)解决方法吗
% Makes \mymacro = \csname
\expandafter\let\expandafter\mymacro\pgfkeysvalueof{<key>}
但你要么必须做二运行次数\expandafter
:
\expandafter\expandafter\expandafter\let
\expandafter\expandafter\expandafter\mymacro
\pgfkeysvalueof{<key>}
(这在很多方面都不好,首先你不必知道如何\pgfkeysvalueof
扩展),或者你只需要知道宏实际上只是\pgfk@<key>
(这很糟糕,因为它暴露了抽象的内部表示)。
但你不想这样做;如果你这么做了,那么你就滥用了pgfkeys
。相反,如果你在系统内工作,你可以使用.get
:
\pgfkeys{<key>/.get = \mymacro}
它执行了上面提到的最后一件事(但现在它很好,因为它pgfkeys
本身使用了自己的内部表示)。我不认为有一种快速的方法可以将一个密钥复制到另一个密钥钥匙但是,.link
处理程序的功能类似于\def
,\let
而\pgfkeyslet{<key>}<macro>
宏则复制宏到某个键。下面是执行此操作的处理程序:
% Uses `etoolbox` for brevity
\pgfkeys{/handlers/.let/.code = {%
\csletcs{pgfk@\pgfkeyscurrentpath}{pgfk@#1}%
\csletcs{pgfk@\pgfkeyscurrentpath/.@cmd}{pgfk@#1/.@cmd}%
}}
/handlers
我认为,如果在命名空间中书写可以让人成为pgfkeys
开发人员,那么这是否邪恶是一个自我问题。
最后,使用 还有一个相关的缺点.initial + \pgfkeysvalueof
,即您无法轻松地\expandafter
生成宏,而且实际上没有办法解决这个问题。也就是说,如果您使用 来.initial
存储一些像宏一样的内容带有参数并且您希望同时将密钥用作宏并将参数存储在另一个宏中,那么您将遇到麻烦。例如,一个密钥模仿\section
:
\pgfkeys{section key/.initial = \section}
% \optarg is either empty or contains [...] material
\pgfkeysvalueof{section key}\optarg\mandarg
(这实际上毫无意义,但可以作为一个例子):这根本行不通,因为\section
需要它的可选参数看起来像一个。所以你通常会写
\expandafter\section\optarg\mandarg
一切都会好起来。不幸的是,同样的方法不适用于section key
:
% Actually does nothing at all
\expandafter\pgfkeysvalueof{section key}\optarg\mandarg
和以前一样,足够多的操作\expandafters
都可以工作,但实际上不存在一个跳过整个代码块来扩展下一个标记的操作,所以你会陷入混乱。然而,如果你只使用,这一切都会很容易.store in
:
\pgfkeys{section key/.store in = \sectionkey}
\pgfkeys{section key = \section}
% No problem
\expandafter\sectionkey\optarg\mandarg
因为pgfkeys
它是一种面向对象的语言,我认为它的一个特性是,即使它确实愿意为其编写的语言提供一个接口,它也很难作为宏语言来运行。
无论如何,如果你要使用宏,那么它显然对.store in
他们来说更高效。因为没有人真正成功地隐藏了 TeX 的基本特性全部编程活动,总是需要在某个时候做一些肮脏的事情,问题是什么时候。我相信这些是唯一需要真正走出pgfkeys
围墙花园的时候。
答案2
值键(由处理程序创建/.initial
)将值保存在名为 的宏中\pgfk@<full path to key>
。最常用的示例可能包括键minimum width
,minimum height
以及 PGF 构造节点时经常读取的内部和外部分隔。除了直接使用之外,\csname pgfk@<full path to key>
还可以通过以下方式访问它们:
\pgfkeysvalueof{<full path to key>}
:这仅仅是访问的包装器
\csname
并且可扩展(当然只要内容可扩展)。\pgfkeysgetvalue{<full path to key>}<macro>
:这会将密钥的内容保存在 中
<macro>
。这是通过\let
宏完成的。key/.get=<macro>
:与 类似
\pgfkeysgetvalue
,/.get
处理程序将其使用的密钥的内容保存在 中<macro>
。 不同之处\pgfkeysgetvalue
在于它可以在 或类似参数内使用\pgfset
,\tikzset
因此\pgfkeys
使用者不需要知道完整和正确的路径。 也可以这样做\pgfkeys{/tikz/my value/.initial=7} \tikzset{my value/.get=\myValue}
而不必在前面添加
/tikz/
。当然,如果经常需要这样做,你可以简单地定义\def\tikzgetvalue#1{\pgfkeysgetvalue{/tikz/#1}}
为了那个原因。
\pgfkeys{<full path of key>}
:/.default
如果值键没有存储值,也可以通过简单地使用不带 的键名来访问该值=
。但是,这是不可扩展的(因此不能用于 PGF 数学)。这样也可以“获取”诸如 之类的键的值以及分隔符
minimum width
,minimum height
因为它们实际上属于该/pgf
路径。(/.get
处理程序既不检查定义的键,也不检查任何路径/.search also
。)
如果用户知道密钥(或密钥路径),他们就可以使用它而不必知道值的实际存储位置。
对于 a /.store in
(或类似的键),键的值(即宏的值)不能被用户直接访问,即使宏可能只是\mystoragemacro
因此很容易使用(没有\makeatletter
或\csname
)。
由于使用两个宏来存储一个值,因此键/.store in
会产生一点开销:
- 存储值的实际宏,例如
\mystoragemacro
;和 - 该键使用的宏,比如说
\pgfk@/tikz/my value/.@cmd
。
如果没有\newcommand*{\mystoragemacro}{}
and my mkey = <initial value>
,则/.store in
密钥可被视为从未定义/从未被用户使用。(尽管你可以改变这一点:如何测试已知键值是否已在 pgfkeys 中设置。
一个/.initial
密钥需要(或多或少)先被获取才能使用(如果考虑\mystoragemacro
和\pgfkeysvalueof{<full path to key>}
有很大不同,第二个密钥只需要扩展两次,然后具有与相同的状态\mystoragemacro
)。
如果您要处理大量/.initial
键的值,您可以考虑通过 以更易于访问的宏名称“获取”它们\pgfkeysgetvalue
。 如果它们包含数学表达式,则在将它们保存在宏中之前让 PGF 数学运行它们可能会更有效率。
结论:方便。
/.store in
如果需要,键值可能更容易访问。我认为键/.initial
更灵活、更易于访问,尤其是对于 TikZ,因为大多数想法都投入到了 PGF 数学中。