保存选择并稍后在 pgfkeys 中执行?

保存选择并稍后在 pgfkeys 中执行?

使用 pgfkeys,我想要一个选择键,它在选择一个值时不是立即执行一些代码,而是将选择存储在某个地方,然后稍后根据需要执行与该选项相关的代码。

举个例子应该能说明问题。我想定义某种处理程序,这样我就可以编写类似以下内容的代码:

\pgfkeys{
  /action/.is choice,
  /action/jump/.on exec = \dojump,
  /action/walk/.on exec = \dowalk,
  /action/run/.on exec  = \dorun
}

这样当我选择一个选项时

\pgfkeys{/action=jump} # remembers that I selected "jump"

相应的代码\dojump不是执行,但选择被保存并稍后执行,直到我实际说出类似以下内容:

\pgfkeys{/action/.exec} # actually does the `\dojump`

这应该允许用户在某个时候选择一个选项,然后稍后改变主意,然后将该选项放回去。但直到我明确要求执行所选选项的代码时,才应该执行任何操作。

有没有一种简单的方法可以向 pgfkeys 添加处理程序并实现此目的?我做事的方式正确吗,还是我在重新发明轮子?

答案1

在对我的原始想法进行了几次迭代之后,我实现了一个我现在非常满意的解决方案,并且它足够稳定,所以我很乐意与大家分享它。

我把它变成了一个小包裹埃兹凯斯,这是一个单独的文件,主要包含文档和一些定义新 pgfkey 处理程序的代码。

作为示例用法,设置后:

\pgfkeys{
  my package/.is family,
  my package,
  align/.is code choice,
  align/left/.code   = \raggedright,
  align/center/.code = \centering,
  align/right/.code  = \raggedleft,
  format/.is code choice,
  format/bold/.code    = \textbf{#1},
  format/italics/.code = \textit{#1},
}

可以使用以下方式选择选项:

\pgfkeys{my package,align=right,format=bold}

这会导致记住所选的选项,但是不是执行。只有在稍后通过明确评估来请求时

\pgfkeys{my package,align/.eval,/format/.eval=Hello}

然后相应的子键将被执行和扩展,在这个例子中是

\raggedleft\textbf{Hello}

答案2

解决方案 (2012/11/02)

我认为我们不需要鍵盤对于这个任务,但这是一个尝试。

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\def\elk@do#1{\@ifnextchar x#1#1}
\def\elk@@do#1#2#3{%
  \csname @\ifx#1#2gobble\else iden\fi\endcsname
  {#3\elk@do#1}%
}
\def\foreachelk#1#2{%
  \def\elkdo##1,{\elk@@do\elkdo{##1}{#2}}%
  \elk@do\elkdo#1,\elkdo,%
}
\def\foreachbison#1#2{%
  \def\bisondo##1/##2,{\elk@@do\bisondo{##1}{#2}}%
  \elk@do\bisondo#1,\bisondo/,%
}
\def\pgfkeys@everystartchoice{}
\def\pgfkeyseverysoc{\g@addto@macro\pgfkeys@everystartchoice}
\def\pgfkeys@everyendchoice{}
\def\pgfkeyseveryeoc{\g@addto@macro\pgfkeys@everyendchoice}
\let\nava@choicewrapper\@iden
\def\choicewrapper{\gdef\nava@choicewrapper}
\pgfkeys{
  /nava/action/.is family,
  /nava/action/.cd,
  .define choices/.code={%
    \foreachbison{#1}{%
      \pgfkeysalso{%
        /nava/action/##1/.code={%
          \ifcsname nava-##1\endcsname
            \@latexerr{Choice '##1' already defined}\@ehd
          \else
            \expandafter\edef\csname nava-##1\endcsname{%
              \unexpanded\expandafter{\pgfkeys@everystartchoice}%
              \unexpanded{\nava@choicewrapper{##2}}%
              \unexpanded\expandafter{\pgfkeys@everyendchoice}%
            }%
          \fi
        },
        /nava/action/##1,
      }%
    }%
  },
  .exec method/.code={%
    \foreachelk{#1}{\csname nava-##1\endcsname}%
  },
}
% \addnewchoices{<choice1/method1,choice2/method2,...>}
\def\addnewchoices#1{\pgfkeys{/nava/action/.define choices={#1}}}
\def\dochoicemethods#1{\pgfkeys{/nava/action/.exec method={#1}}}
\makeatother

\begin{document}
% These are optional. Insert at end of every choice:
\pgfkeyseveryeoc{.\endgraf}
% Wrap the methods for the choices:
\choicewrapper{\textit}

% Define and remember my choices:
\addnewchoices{jump/I'll jump,walk/I'll walk,run/I'm running home}

% Call my choices and execute their methods:
\dochoicemethods{jump,walk,run,run,walk}
\dochoicemethods{jump}
\end{document}

答案3

.style通过将所需的代码分配给辅助键,可以轻松实现这种延迟评估.exec

\documentclass{article}
\usepackage{pgfkeys}
\begin{document}

\newcommand\dojump{jump}
\newcommand\dowalk{walk}
\newcommand\dorun{run}

\pgfkeys{
  /action/.is choice,
  /action/jump/.style={/action/.exec/.code={\dojump}},
  /action/walk/.style={/action/.exec/.code={\dowalk}},
  /action/run/.style={/action/.exec/.code={\dorun}}
}

\pgfkeys{/action=jump} % remembers that I selected "jump"

\pgfkeys{/action/.exec} % actually does the `\dojump`

\end{document}

答案4

这个问题问得非常好。在我目前的项目中,我经常遇到需要这种行为的情况。不过,我认为可能存在比你提出的更简单的解决方案。

下面是它的工作原理:

  • 首先,我们定义一个新的处理程序,可以像 一样使用/some/key/.is deferred choice。这将执行/some/key/.is choice并定义一个'镜像键' /deferred choice/code/some/key/.is choice。此镜像键将具有相同的选择,它将存储实际的代码。
  • 然后用 定义各个选项/some/key/<choice>/.deferred code={<code>}。这会将 存储<code>在 中/deferred choice/code/some/key/<choice>/.code,并设置/some/key/<choice>/.code为仅存储值以供以后使用。
  • 设置时,使用/some/key=<choice>将值<choice>存储在键中。/deferred choice/value/some/key\pgfkeyssetvalue
  • 在运行时,会加载来自的/some/key/.execute=#1值,以便随后找到并执行键。<choice>/deferred choice/value/some/key/deferred choice/code/some/key/<choice>={#1}

下面是示例的编译输出。如您所见,设置align=right, format=bold没有输出任何内容,因为实际代码的执行被推迟到.execute运行时。

示例的编译输出

\documentclass{article}
\usepackage{pgfkeys}

\makeatletter
\pgfkeysdef{/handlers/.deferred code}{% is called with \pgfkeyscurrentpath=/path/ChoiceKey/ChoiceValue
    \pgfkeysdef{/deferred choice/code\pgfkeyscurrentpath}{#1}%
    \edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}\pgfkeys@split@path% now path=/path/ChoiceKey and name=ChoiceValue
    \pgfkeysedef{\pgfkeyscurrentkey}{\noexpand\pgfkeyssetvalue{/deferred choice/value\pgfkeyscurrentpath}{\pgfkeyscurrentname}}%
}
\makeatother
\pgfkeysdef{/handlers/.is deferred choice}{%
    \expandafter\pgfkeysedef{\pgfkeyscurrentpath/.execute}{% is called with \pgfkeyscurrentpath=/path/ChoiceKey
        \noexpand\pgfkeysgetvalue{/deferred choice/value\pgfkeyscurrentpath}{\noexpand\temp}%
        \noexpand\pgfkeysalso{/deferred choice/code\pgfkeyscurrentpath/\noexpand\temp={##1}}%
    }
    \pgfkeysalso{\pgfkeyscurrentpath/.is choice}%
    \pgfkeysalso{/deferred choice/code\pgfkeyscurrentpath/.is choice}%
}

\begin{document}
  \pgfkeys{
      /my package/.is family,
      /my package,
      align/.is deferred choice,
      align/left/.deferred code   = left:,
      align/right/.deferred code  = right:,
      format/.is deferred choice,
      format/bold/.deferred code    = bold:{#1},
      format/italics/.deferred code = italics:{#1},
  }
  \pgfkeys{/my package, align=right, format=bold}
  \pgfkeys{/my package, align=left, format=italics}
  %\pgfkeys{/my package, align=wrong} % Error: Choice 'wrong' unknown in choice key '/my package/align'
  \pgfkeys{/my package, align/.execute, format/.execute=Hello}
\end{document}

相关内容