我想我终于对 有了合理的理解pgfkeys
,但使用或不使用处理程序初始化密钥.initial
给我带来了麻烦。我确实理解.default
和之间的区别.initial
,前者在没有为密钥指定值时指定一个值,而后者在其他情况下指定一个值(无论如何,这是我的理解)。 MWE 包含四个示例案例。
第一个似乎可以正常工作,只是尝试使用 初始化paintwhat
并paintshade
会.initial
给出有关未定义控制序列的错误。第二个示例似乎可以正常工作,但再次尝试使用 while 初始化units
似乎.initial
可以.default
完美工作。在第三个示例中,我理解 不适.initial
用于子密钥,我认为这就是我无法在此示例中使用它的原因。那么,我该如何初始化sunits
?最后,在第四个示例中,当我zunits
在 LAST 中初始化 时\pgfkeys{...}
, 的初始使用\picktheunits
按预期工作,但最终使用却不行。
最终,我的问题是:初始化密钥时出现这些困难的原因是什么?是我忽略了一些琐碎的事情,还是一些微妙的事情?
(如果有人好奇,这个带有单位的例子是我测试键和值的典型测试用例。)
以下是 MWE:
% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode
\documentclass{article}
\usepackage{pgfkeys}
\begin{document}
A command about painting things.\par
%\newcommand*{\paintit}{} % this appears to be optional
%\newcommand*{paintshadw}{} % this appears to be optional
\pgfkeys{%
/paintthe/.is family, /paintthe,
paintwhat/.store in=\paintit,
paintwhat/.default=building, % used when just paintwhat is given
paintwhat = barn, % initial value of paintwhat; can be overwritten
paintshade/.store in=\paintshade,
paintshade/.default=light, % used when just paintshade is given
paintshade = light, % initial value of paintshade; can be overwritten
}%
% version with shade
\newcommand*{\paintthecolor}[2][]{%
\begingroup % make key values local to this instance
\pgfkeys{/paintthe,#1}
Paint the \paintit\ \paintshade\ #2.
\endgroup
}%
\paintthecolor{green}\par
\paintthecolor[paintwhat]{green}\par
\paintthecolor[paintwhat=house,paintshade=dark]{red}\par
\paintthecolor[paintwhat=car,paintshade=medium]{blue}\par
\paintthecolor{gray}\par
% These commands will already be defined, albeit not as they are here.
\newcommand*{\perpusederivedunits}{You'll get derived units.}
\newcommand*{\perpusebaseunits}{You'll get base units.}
\newcommand*{\perpusealternateunits}{You'll get alternate units.}
A command about selecting default units. Seems to work correctly.\par
\pgfkeys{%
/chooseunits/.is family, /chooseunits,
units/.store in=\chosenunits,
units/.default=alternate,
units=derived, % .initial doesn't work here
}%
\newcommand*{\choosetheunits}[1][]{%
\begingroup
\pgfkeys{/chooseunits,#1}
\csname perpuse\chosenunits units\endcsname % this builds the command that selects the units
\endgroup
}%
\choosetheunits\par % works correctly
\choosetheunits[units=base]\par
\choosetheunits[units]\par
\choosetheunits[units=derived]\par
\choosetheunits[units=alternate]\par
\choosetheunits[units=derived]\par
\choosetheunits[units]\par
\choosetheunits\par % works correctly
A different approach using \texttt{.is choice} handler. Can't initialize.\par
\pgfkeys{%
/selectunits/.is family, /selectunits,% /.cd,
sunits/.is choice,
sunits/.initial={\perpusederivedunits}, % .initial doesn't work with .is choice
sunits/.default=alternate,
sunits/base/.code={\perpusebaseunits},
sunits/derived/.code={\perpusederivedunits},
sunits/alternate/.code={\perpusealternateunits},
}%
\newcommand*{\settheunits}[1][]{%
\begingroup
\pgfkeys{/selectunits,#1}
\endgroup
}%
\settheunits\par % doesn't work since .initial doesn't apply to .is choice
\settheunits[sunits=base]\par
\settheunits[sunits]\par
\settheunits[sunits=derived]\par
\settheunits[sunits=alternate]\par
\settheunits[sunits=derived]\par
\settheunits[sunits]\par
\settheunits\par % doesn't work since .initial doesn't apply to .is choice
Another approach to the unit problem. Partially works.\par
\pgfkeys{%
/pickunits/.is family, /pickunits,
%zunits=derived, % throws unknown key error when placed here
zunits/.default=alternate,
zunits/.code={\csname perpuse#1units\endcsname},
zunits=derived, % works partially when placed here
}%
\newcommand*{\picktheunits}[1][]{%
\begingroup
\pgfkeys{/pickunits,#1}
\endgroup
}%
\picktheunits\par % works correctly
\picktheunits[zunits=base]\par
\picktheunits[zunits]\par
\picktheunits[zunits=derived]\par
\picktheunits[zunits=alternate]\par
\picktheunits[zunits=derived]\par
\picktheunits[zunits]\par
\picktheunits\par % does not work
\end{document}
答案1
密钥处理程序.initial
用于存储值里面的名称空间中的键pgfkey
。如果某个键除了该处理程序之外没有其他关联的处理程序.initial
,则它将变为相似的到一个.store in
键,它将存储新的值里面使用时使用 key。然后可以通过.get
处理程序或使用检索存储的值\pgfkeysvalueof
。
当您还使用.code
键时,将继续存储最后存储的值(无论是在添加 之前.code
,还是在稍后使用 时.initial
),但现在.code
当使用键时(没有处理程序) 优先。因此,当您现在执行 时,\pgfkeys{/foo=abc}
它不会存储abc
,而是.code
执行abc
。
任何(或大多数?)其他处理程序都在内部使用.code
,因此一旦您向键添加另一个处理程序,它就会失去在正常使用中将值存储在键本身内部的功能。
但是,就您而言,您从未打算使用 的存储功能pgfkeys
,而是使用诸如 之类的处理程序.store in
。因此,您不会像使用其他一些 key=value 包(如或)那样使用.initial
inside作为初始值。相反,使用诸如或 之类的处理程序为键设置初始值的正确方法是仅使用键,例如,使用。pgfkeys
l3keys
expkv-def
.choice
.store in
\pgfkeys{/foo=bar}
顺便说一句,您未定义的控制序列源于.store in
未初始化所使用的宏,因此在\pgfkeys{/foo/.store in=\foo}
宏\foo
尚未定义后,它只会在您第一次使用该键后被定义。
为通过以下方式定义的宏定义初始值的另一种方法.store in
是使用\newcommand*
:
\newcommand*\foo{bar}
\pgfkeys{/foo/.store in=\foo,/foo/.default=baz}
将添加一个/foo
存储在里面的键\foo
,具有初始值bar
和默认值baz
。
答案2
经过更多的实验,我终于找到了一个我认为不错的解决方案,即使用\NewDocumentCommand
。有两个\pfgkeys{...}
块,一个使用.is choice
,一个不使用。显然,一次只能使用其中一个,但两者都给出相同的结果,并且新命令的语法完全符合我的预期。这个想法是测试包含键的可选参数是否存在,或者是否存在,并执行相应的代码。我最终希望尽可能\NewDocumentCommand
多地使用,所以这似乎是一个不错的解决方案。我想有人会让我知道我是否犯了任何暴行。
编辑:我添加了对键值的健全性检查。
以下是 MWE:
% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode
\documentclass{article}
\usepackage{pgfkeys}
\begin{document}
\newcommand*{\perpusebaseunits}{You'll get base units.}
\newcommand*{\perpusederivedunits}{You'll get derived units.}
\newcommand*{\perpusealternateunits}{You'll get alternate units.}
% This block does not use .is choice
\pgfkeys{%
/selectunits/.is family, /selectunits,%
sunits/.initial=alternate,%
sunits/.default=derived,%
sunits/.code={%
\ifcsname perpuse#1units\endcsname
\csname perpuse#1units\endcsname
\else
\GenericError{}
{\MessageBreak settheunits: Illegal key value}
{Key 'sunits' can only be one of base, derived, or alternate.}
{Read the documentation for help.}
\fi
}%
%sunits/.code={\csname perpuse#1units\endcsname},%
}%
% This block uses .is choice
%\pgfkeys{%
% /selectunits/.is family, /selectunits,%
% sunits/.is choice,%
% sunits/.initial=alternate,%
% sunits/.default=derived,%
% sunits/base/.code={\perpusebaseunits},%
% sunits/derived/.code={\perpusederivedunits},%
% sunits/alternate/.code={\perpusealternateunits},%
%}%
\NewDocumentCommand{\settheunits}{ o }{%
\IfValueTF {#1}
{ \pgfkeys{/selectunits,#1} }
{ \csname perpuse\pgfkeysvalueof{/selectunits/sunits}units\endcsname }
}%
\settheunits\par
\settheunits[sunits]\par
\settheunits[sunits=base]\par
\settheunits[sunits=derived]\par
\settheunits[sunits=alternate]\par
\settheunits[sunits]\par
\settheunits\par
\settheunits[sunits=blubb]\par % This will halt processing with an error.
\end{document}