pgfkeys 的 '.is if' 处理程序

pgfkeys 的 '.is if' 处理程序

让我们从 pgf 手册 2.10 版第 495 页中的一个例子开始:

\pgfkeys{/flat world/.is if=theworldisflat}
\pgfkeys{/flat world=false}
\def\x#1{% code-A
  \iftheworldisflat
    Flat
  \else
    Round?
  \fi
  #1%
}

对于这个例子我有两个问题。

(1)我可以\newif\iftheworldisflat在命令中设置\pgfkeys,或者更好的是,让\pgfkeys系统自动为我声明布尔值。后者是在键值包裹。

(2) 如何将代码(例如上面的代码 A)直接传递给键flat world\pgfkeys而不绕道?这可能吗?我认为下面的方法更好,因为它允许在设置键时执行回调。

\pgfkeys{%
  /my family/my keys/.cd,
  keya/.is if={<boolean>}{<callback>},
}

(3)为什么鍵盤在这里使用\edef

\def\pgfkeys@non@outer@newif@#1#2{%
   \expandafter\edef\csname #2true\endcsname
     {\noexpand\let\noexpand#1=\noexpand\iftrue}%
   \expandafter\edef\csname #2false\endcsname
     {\noexpand\let\noexpand#1=\noexpand\iffalse}%
   \csname #2false\endcsname
}

编辑

我尝试过类似下面的方法。我仍在考虑如何优化代码;这就是为什么我还没有发布它:

\pgfkeys{%
  /my family/my keys/.cd,
  keya/.is if=boola,
  keyb/.is if=keyb, % Acceptable, but why repeat 'keyb'?
  % Use 'keyc' as the boolean and allow the user to provide a default value.
  % No need to provide a boolean or repeat 'keyc' on the right hand side of '='.
  keyc/.is bool={true}{\def\y##1{##1-True-#1}}{\def\y##1{##1-False-#1}},
}

答案1

与 不同xkeyvalpgfkeys不仅设计用于 LaTeX。在纯格式中,\newif位于外部,因此无法(轻松)在 内使用。因此,您必须在使用 设置的 keyval 之外\pgfkeys设置宏。\if...pgfkeys

关于第二个问题,处理程序的工作方式并非如此.is if。当然,您可以重新定义它(非常冒险),或者更安全地创建自己的处理程序来执行您想要的操作。

在第三部分中,使用\noexpandinside\edef是将外部控制序列放入其他宏的一种方式,这通常是不允许的。再次强调,这更多地是针对纯 TeX 而非 LaTeX 或 ConTeXt 的安全预防措施。

答案2

我也想了很长时间才找到一个更好的.is if解决方案,这样每次我想创建一个新的布尔选项时,就不需要再发明一个新名字了/my package/fancy option名字很不错,不会与其他包冲突,而且绝对比\if@mpkg@@fan@opt到处写要好。此外,作为一项不错的附加功能,您还可以定义选项,使一个切换项的值依赖于另一个切换项(见下面的示例)。

在切换的基础上etoolbox,我将解决方案实现为一个新的.is toggle处理程序,并创建了一个小包埃兹凯斯它基本上是一个包含大量注释和一些定义新 pgfkey 处理程序的代码的单独文件。

作为一个示例用法,切换定义使用

\pgfkeys{
  my package/.is family,
  my package,
  numbered/.is toggle,
  in toc/.is toggle,
  in toc/if numbered/.code = \linktoggle{/my package/in toc}{/my package/numbered}
}

然后照常选择选项

\pgfkeys{
  my package,
  numbered = false,
  in toc = if numbered % will be true or false depending on `numbered`
}

然后可以编写如下条件:

\iftoggle{/my package/numbered}{numbered is true}{numbered is false}
\iftoggle{/my package/in toc}{in toc is true}{in toc is false}

答案3

以下是我建议的.is if处理程序。代码位于pgfkeys 补丁是编译以下内容所必需的。

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{pgfkeys-patch}
\makeatletter
% Syntaxes:
% <key>.is if with no callback=<default>
% <key>.is if with 1 callback={<default>}{<callback>}
% <key>.is if with 2 callbacks={<default>}{<callback-1>}{<callback-2>}
% In this way I know I am dealing with <key> alone, and not with another 
% (auxiliary) boolean as in the conventional '.is if' handler.
\pgfkeys{%
  /handlers/.is if with no callback/.code={%
    \pgfkeys@boolean@handler{#1}{##1}{}{}%
  },
  /handlers/.is if with 1 callback/.code 2 args={%
    \pgfkeys@boolean@handler{#1}{##1}{#2}{}%
  },
  /handlers/.is if with one callback/.style 2 args={%
    \pgfkeyscurrentpath/.is if with 1 callback={#1}{#2}%
  },
  /handlers/.is if with 2 callbacks/.code n args={3}{%
    \pgfkeys@boolean@handler{#1}{##1}{#2}{#3}%
  }
}
\def\pgfkeys@savresname#1{%
  \ifcase#1\relax
    \let\pgfkeyssavedkey=\pgfkeyscurrentkey
    \let\pgfkeyssavedname=\pgfkeyscurrentname
    \edef\pgfkeyssavedpath{\pgfkeyscurrentpath}%
    \edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}%
    \pgfkeys@split@path
    \let\pgfkeyssavednameb\pgfkeyscurrentname
  \or
    \let\pgfkeyscurrentkey=\pgfkeyssavedkey
    \let\pgfkeyscurrentname=\pgfkeyssavedname
    % Don't do \edef\pgfkeyscurrentpath{\pgfkeyssavedpath} here, since
    % \pgfkeyscurrentpath is determined by \pgfkeys@pathtoks.
  \else
    \pgfkeys@error{Number '#1' out of range [0,1]}%
  \fi
}
\def\pgfkeys@boolean@handler#1#2#3#4{%
  \pgfkeys@savresname{0}%
  % Let's not redefine 'if\pgfkeyscurrentname' if it already exists,
  % so that we don't change its current state:
  \ifpgfkeyscsdef{if\pgfkeyscurrentname}{}{%
    % Can use \outer-defined \newif:
    \csname newif\expandafter\endcsname
    \csname if\pgfkeyscurrentname\endcsname        
  }%
  \begingroup
  \def\reserved@a{\pgfkeyssavedpath/.code=}%
  \expandafter\expandafter\expandafter\endgroup\expandafter\expandafter
  \expandafter\pgfkeysalso\expandafter\expandafter\expandafter{%
    \expandafter\reserved@a\expandafter\pgfkeys@boolean@handler@b
      \expandafter{\pgfkeyssavednameb}{#2}{#3}{#4},
    \pgfkeyssavedpath/.default=#1%
  }%
  \pgfkeys@savresname{1}%
}
\def\pgfkeys@boolean@handler@b#1#2#3#4{%
  \ifpgfkeysxin{,\pgfkeystrimspace{#2},}{,true,false,}{%
    \ifpgfkeyscsdef{#1#2}{%
      \csname#1#2\endcsname
      \ifpgfkeysblank{#3#4}{}{\ifpgfkeysbool{#1}{#3}{#4}}%
    }{%
      \pgfkeysvalueof{/errors/boolean expected/.@cmd}%
        \pgfkeyscurrentkey{#2}\pgfeov
    }%
  }{%
    \pgfkeysvalueof{/errors/boolean expected/.@cmd}%
      \pgfkeyscurrentkey{#2}\pgfeov
  }%
}
\makeatother

例子:

\pgfkeys{%
  /my family/.cd,
  flatworlda/.is if with no callback=true,
  flatworldb/.is if with 1 callback={true}{\def\x##1{##1T#1}},
  flatworldc/.is if with 2 callbacks={false}{\def\x##1{##1T#1}}{\def\x##1{##1F#1}},
  % pgfkeys package doesn't seem to like the idea of optional argument, otherwise I
  % would have preferred something like the following, in which the default 
  % value 'true' is optional:
  % flatworlda/.is if with no callback=[true]
}

\begin{document}
\pgfkeys{/my family/flatworlda=false}
\pgfkeys{/my family/flatworldb=true}
\pgfkeys{/my family/flatworldc=false}
\edef\x{%
%  \ifflatworlda
  \ifflatworldb
%  \ifflatworldc
    Is flat%
  \else
    Is round%
  \fi
}
\pgfkeys{/my family/flatworldb=true}
\ifflatworldb
  true
\else
  false
\fi
\end{document}

相关内容