本着 pgfkeys 的精神:.list 处理程序

本着 pgfkeys 的精神:.list 处理程序

这个问题的简单答案似乎是“不”;引用 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}

这里我隐式地使用了\globaldefs0(或非正数),因此赋值\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}

相关内容