这个问题的简单答案似乎是“不”;引用 pgf 手册:
键的设置始终是当前 TeX 组的本地设置。
然而,这非常烦人,因为这意味着你不能做类似的事情:
\foreach \mykey/\myvalue in {
long/list,
of/keys%
} {
\pgfkeyssetvalue{\mykey}{\myvalue}
}
所以我的下一个最佳选择是:
\makeatletter
\pgfkeys{/applicant/.code 2 args={\expandafter\global\expandafter\def\csname applicant@#1\expandafter\endcsname\expandafter{#2}}}
\foreach \mykey/\myvalue in {
long/list,
of/keys%
} {
\pgfkeys{/applicant={\mykey}{\myvalue}}
}
\makeatletter
但这不太透明,因为要得到“值”,我必须使用宏\csname applicant@whatever the key name was\endcsname
。(另外,我不太事实由于pgfkeys
这是我的第一次尝试,所以我应该使用类似/.expand once
或的东西/.store in
,但那是顺便的。)
有没有更好的方法来规避这个限制?我很高兴有一个新的宏定义关键,所以\pgfglobalkeys
会没问题(虽然在精神上\pgfkeys
感觉更像是应该如此,\pgfkeys{key name/.global=...}
但我们不要争论),但当使用那么密钥的值应该可以通过传统方式访问,比如说pgfkeysgetvalue
。
答案1
编辑:我刚刚收到我的副本TeX 按主题分类来自 Lulu,这当然意味着我的工作日提前结束了 :-)。我偶然发现了\globaldefs
,它允许人们用“是”来回答实际提出的问题:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\pgfkeys{/tmp/.cd, foo/.initial = a, bar/.initial = z}
\def\showstatus{%
(level: \the\currentgrouplevel\ --
globaldefs: \the\globaldefs\ --
foo: \pgfkeysvalueof{/tmp/foo} --
bar: \pgfkeysvalueof{/tmp/bar})}
\showstatus
{\globaldefs=1\relax
\foreach \k/\v in {{/tmp/foo}/bb,{/tmp/bar}/yy} { %
\pgfkeyssetvalue{\k}{\v}
}
\showstatus
}
\showstatus
\end{document}
这里我隐式地使用了\globaldefs
0(或非正数),因此赋值\globaldefs=1
是局部的。如果\globaldefs
在当前范围内已经是正数,我们不需要设置它;事实上这样做是错误的(因为它是值可能对某些周围组来说是局部的;再次赋予它一个正值会将其设置为全局的)。纠正这个问题留作练习。
原始答案安德鲁,你关于使用的评论\aftergroup
让我开始调查。事实证明,的主体\foreach
实际上是执行的二级别降低。假设您真正想要的是一种能够\foreach
在当前范围内设置键的方法(而不是实际全局设置它们),这似乎有效:
\documentclass{article}
\usepackage{tikz}
\usepackage{etoolbox}
\begin{document}
\pgfkeys{/tmp/.cd, foo/.initial = a, bar/.initial = z}
\def\showstatus{%
(\the\currentgrouplevel\ --
\pgfkeysvalueof{/tmp/foo} --
\pgfkeysvalueof{/tmp/bar})}
\makeatletter
\csdef{my@count}{0}
\newcommand*{\@csgincr}[1]{\csnumgdef{#1}{\csuse{#1} + 1}}
The initial status: \showstatus
\foreach \k/\v in {{/tmp/foo}/bb,{/tmp/bar}/yy} { %
\@csgincr{my@count}%
% \showstatus (\k, \v, \my@count)
% Define a global macro which does the keyval-setting (locally), and
% which then undefines itself
\csxdef{@tmp@setkeyval@\my@count}{\noexpand\pgfkeyssetvalue{\k}{\v}%
\noexpand\global\noexpand\csundef{@tmp@setkeyval@\my@count}}%
% Define a global macro which when called, places the above
% \aftergroup, and then undefines itself
\csxdef{@tmp@export@\my@count}{\noexpand\aftergroup%
\expandafter\noexpand\csname @tmp@setkeyval@\my@count\endcsname%
\noexpand\global\noexpand\csundef{@tmp@export@\my@count}}%
% Place the above \aftergroup
\expandafter\aftergroup\csname @tmp@export@\my@count\endcsname%
% \aftergroup\par
}
Now we have: \showstatus
{
Now we are on a level 1 group \showstatus
\foreach \k/\v in {{/tmp/foo}/ccc,{/tmp/bar}/xxx} { %
\@csgincr{my@count}%
% \showstatus (\k, \v, \my@count)
\csxdef{@tmp@setkeyval@\my@count}{\noexpand\pgfkeyssetvalue{\k}{\v}%
\noexpand\global\noexpand\csundef{@tmp@setkeyval@\my@count}}%
\csxdef{@tmp@export@\my@count}{\noexpand\aftergroup%
\expandafter\noexpand\csname @tmp@setkeyval@\my@count\endcsname%
\noexpand\global\noexpand\csundef{@tmp@export@\my@count}}%
%
\expandafter\aftergroup\csname @tmp@export@\my@count\endcsname%
% \aftergroup\par
}
The keys have been updated \showstatus
}
but only inside the group \showstatus
\makeatother
\end{document}
答案2
我认为它pgfkeys
不允许开箱即用的全局分配。但是,您可以定义自己的处理程序来定义全局值。问题是,它对所有宏(如、、等\pgfkeyssetvalue
键)的工作方式不同。您必须查找它们的定义并定义这些处理程序的全局版本,如、、。.code
.store in
.style
\pgfkeysgsetvalue
.gcode
.gstore in
.gstyle
.gcode
以下是和的实现\pgfkeysgsetvalue
:
\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\pgfkeysdef{/handlers/.gcode}{%
\long\def\pgfkeys@temp ##1\pgfeov{#1}%
\global\pgfkeyslet{\pgfkeyscurrentpath/.@cmd}{\pgfkeys@temp}%
}
\newcommand{\pgfkeysgsetvalue}[2]{%
\pgfkeys@temptoks{#2}%
\expandafter\xdef\csname pgfk@#1\endcsname{\the\pgfkeys@temptoks}%
}
\makeatother
\begin{document}
\pgfkeys{test/.code={\message{NOOO}}}%
\pgfkeyssetvalue{test2}{\message{NOOO}}%
{%
\pgfkeys{test/.gcode={\message{WORKS}}}%
\pgfkeysgsetvalue{test2}{\message{WORKS AS WELL}}%
}
\pgfkeys{test}
\pgfkeysgetvalue{test2}{\test}\test
\end{document}
答案3
首先,您要使用\pgfkeyslet
而不是\pgfkeyssetvalue
,因为您要使用外部的键,其中\myvalue
不再定义,因此您必须将键设置为 的内容\myvalue
而不是将其设置为其\myvalue
自身。其次, 的定义\pgfkeyslet
以 开头\expandafter\let
。实际上, 前缀\pgfkeyslet
仍然\global
会影响\let
之后的expandafter
,因此
\foreach \mykey/\myvalue in {long/list, of/keys}
{\global\pgfkeyslet{\mykey}{\myvalue}}
按照你的要求来做。这个解决方案有点不妥,因为它依赖于 的内部定义\pgfkeyslet
。
另一个解决方案是忘记\foreach
它是否不符合要求,并使用另一个迭代逗号分隔列表的方法。如果您可以自由选择要迭代的列表的表示形式,最简单的方法是 Knuth 在 TeXbook 附录 D 中提出的方法。将列表定义为
\newcommand\mylist{\li{long}{list}\li{of}{keys}}
\let
现在,您可以通过执行实现循环体的命令来重复处理列表项\li
。在您的示例中,这只是
\let\li\pgfkeyssetvalue
之后
\mylist
处理键值对。(Knuth 建议使用\\
而不是\li
,这在 LaTeX 中可能很危险。)
答案4
本着 pgfkeys 的精神:.list 处理程序
假设您真正想要的是一种循环机制来在当前分组级别设置键(而不是实际全局设置它们),我猜.list
处理程序就是您正在寻找的。
使用它来处理诸如这样的键set key value pair/.style 2 args={#1={#2}}
就可以了。您可以根据自己的喜好调整参数模式(无论如何,foreach 语句的分隔符 / 可能会与第一个参数的键路径符号混淆)。
尽管处理程序的手册上.list
说
使用 foreach 语句处理值列表,因此您可以使用 ... 符号,
似乎这个处理程序并没有把它的每次执行命令在 TEX 组中,与foreach
语句不同。
\documentclass{article}
\usepackage{tikz}
\begin{document}
\pgfkeys{/mykeyspath/.cd, foo/.initial = a, bar/.initial = x}
\def\showstatus{%
[group level=\the\currentgrouplevel , %
foo=\pgfkeysvalueof{/mykeyspath/foo}, %
bar=\pgfkeysvalueof{/mykeyspath/bar}]}
Initial status of two value storing keys: \showstatus .
The problem.
The status during the foreach loop shows that
each execution is actually performed two grouping levels down,
and the effect of the pgfkeys commands only lasts one iteration:%
\foreach \mykey/\myvalue in {foo/b,bar/y} {
\pgfkeyssetvalue{/mykeyspath/\mykey}{\myvalue}\showstatus }.
Indeed, after completion of the foreach loop,
the status is back to initial: \showstatus .
\pgfkeys{set key value pair/.style 2 args={#1={#2}}}% preambule matter
\pgfkeys{%
/mykeyspath/.cd,% sets the default path to the common part of listed keys
/set key value pair/.list={{foo}{b},{bar}{y}}% calls a loop on this list
}
The remedy.
After using the .list handler on a suitable key,
the status is modified as expected: \showstatus .
\end{document}