\ifblank 无法与 \pgfkeys 正确配合使用

\ifblank 无法与 \pgfkeys 正确配合使用

下面的宏\test用于测试它的参数是否为blank,但它给出了意想不到的结果。

我的代码有什么问题?

例子:

\documentclass{article}
\usepackage{tikz,etoolbox}
\begin{document}
\pgfkeys{aaa/.initial={}}
\newcommand{\test}[1]{%
  \expandafter\ifblank\expandafter{#1}{blank}{#1}%
}
<\pgfkeysvalueof{/aaa}>% Typeset "<>"
|\test{\pgfkeysvalueof{/aaa}}|%I think it should be "|blank|", but actually it typesets "||". Why?
\end{document}

答案1

\pgfkeysvalueof{<full key>}需要三次扩展

根据其定义,需要进行三次扩展才能\pgfkeysvalueof{<full key>}扩展为 中保存的值<full key>

% run `latexdef -p pgfkeys -s \pgfkeysvalueof` and you'll get

% pgfkeys.code.tex, line 172:
\def\pgfkeysvalueof#1{\csname pgfk@#1\endcsname}
  • 第一步,\pgfkeysvalueof扩展为其替换文本,其形式为\csname ... \endcsname
  • 第二步,\csname ... \endcsname扩展为一个保存 值的控制序列<full key>。对于/aaa,它是\pgfk@/aaa
  • 第三步,该控制序列扩展为其替换文本。对于/aaa,扩展结果为空。

的确切定义\pgfkeysvalueof将在下一版本中发生变化,请参阅https://github.com/pgf-tikz/pgf/pull/1132,但步数保持不变。

所以

\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{\pgfkeysvalueof{/aaa}}

作品。

\pgfkeysValueOf只需两个步骤

一般来说,

  • 情况 1:如果要测试的字符串宏的替换文本,然后 OP\test对这个宏的作品,如\test{\cmd}
  • 情况 2:如果要测试的字符串是(有限步骤)扩张替换宏的文本,那么理论上我们可以\cmdExpanded基于 定义一个命令,\cmd只需要两次扩展。也就是说, \expandafter\test\expandafter{\cmdExpanded}}}有效。

下面提供的例子\pgfkeysValueOf属于情况2。

另外,如果可扩展性不是重点,那么pgfkeys也是 \pgfkeysgetvalue{<full key>}{<macro>}另一种选择,例如

\pgfkeysgetvalue{/aaa}{\temp} % here \temp belongs to case 1
|\test{\temp}|

示例,使用测试用例来区分“空白值”和“扩展为空白的非空白值”:

\documentclass{article}
\usepackage{tikz,etoolbox}

% OP's definiton
\newcommand{\test}[1]{%
  \expandafter\ifblank\expandafter{#1}{blank}{#1}%
}

% egreg's answer
\ExplSyntaxOn
\NewExpandableDocumentCommand{\testExpl}{m}
  {
    \tl_if_blank:eTF { #1 } {blank} {#1}
  }
\ExplSyntaxOff

% wipet's answer
\def\testExpanded#1{%
  \expandafter\ifblank\expandafter{\expanded{#1}}{blank}{#1}}

% joseph's answer
\newcommand{\testRomannumeral}[1]{%
  \expandafter\ifblank\expandafter{\romannumeral-`Q#1 }{blank}{#1}%
}

% my solution 1, expand three steps 
\newcommand{\testThreeExps}[1]{%
  \expandafter\expandafter\expandafter\test
  \expandafter\expandafter\expandafter{#1}%
}

\begin{document}
\ttfamily

% three tests
\pgfkeys{
  aaa/.initial={},       % blank: empty
  bbb/.initial={{ }},    % blank: a space
  ccc/.initial={\empty}, % non-blank: expand to empty
  ddd/.initial={\space}, % non-blank: expand to a space
  eee/.initial={eee},    % non-blank: expand to non-blank
}


\leavevmode\llap{Expected}%
\foreach \j in {aaa, bbb, ccc, ddd, eee} {%
  % use \pgfkeysgetvalue, then one step expansion
  \pgfkeysgetvalue{/\j}{\temp}%
  \makebox[2cm]{|\test{\temp}|}%
}\bigskip

\foreach \i in {\test, \testExpl, \testExpanded, \testRomannumeral, \testThreeExps} {%
  \leavevmode\llap{\detokenize\expandafter{\i}}%
  \foreach \j in {aaa, bbb, ccc, ddd, eee} {%
    \makebox[2cm]{|\i{\pgfkeysvalueof{/\j}}|}%
  }
  \par
}

% \pgfkeysValueOf{<full key>} takes one step to expansion to <full key>'s value
\newcommand{\pgfkeysValueOf}[1]{%
  \expanded{\unexpanded
    \expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter{\pgfkeysvalueof{#1}}}%
}

\leavevmode\hspace*{-4cm}%
\verb|\expandafter\test\expandafter{\pgfkeysValueOf{<full key>}}|\par%
\foreach \j in {aaa, bbb, ccc, ddd, eee} {%
  \makebox[2cm]{|\expandafter\test\expandafter{\pgfkeysValueOf{/\j}}|}%
}

\end{document}

在此处输入图片描述

注意,测试用例区分了“空白键值”和“扩展为空白的非空白键值”。/aaa和的值/bbb都是空白,但/ccc和的值/ddd都不是空白,尽管它们都扩展为空白。

答案2

\pgfkeysvalueof无法一次性展开,因此\expandafter这里不能使用单个方法。您需要强制展开:最简单的方法是使用 '罗马数字技巧':

\newcommand{\test}[1]{%
  \expandafter\ifblank\expandafter{\romannumeral-`Q#1 }{blank}{#1}%
}

答案3

rommannumeral 技巧会进行完全扩展,直到出现无法扩展的内容。但如果参数是格式bla\macro,并且\macro扩展为,nk则 rommannumeral 技巧会失败。因此,我建议使用\expanded原始:

\def\test#1{\expandafter\ifblank\expandafter{\expanded{#1}}{blank}{#1}}

答案4

您可以做的另一件事是测试键是否排版为 0 号框(这是一个混合答案,这里有 XY 问题)。

但请注意,明确的空间与空白内容不同......

\documentclass{article}
\usepackage[T1]{fontenc}%<- you forgot this!
\usepackage{tikz,etoolbox}
\newsavebox{\measbox}
\newcommand{\test}[1]{%
    \savebox{\measbox}{#1}%
    \ifdim\wd\measbox>0pt \usebox\measbox \else blank\fi
}
\begin{document}
\pgfkeys{aaa/.initial={}}
<\pgfkeysvalueof{/aaa}>% Typesets "<>" (with the T1 fontenc, otherwise ¡?)

|\test{\pgfkeysvalueof{/aaa}}|% typesets |blank|

\pgfkeys{aaa=\bfseries}

|\test{\pgfkeysvalueof{/aaa}}|% typesets |blank|

\pgfkeys{aaa=~}

|\test{\pgfkeysvalueof{/aaa}}|% typesets | |

\pgfkeys{aaa=\bfseries ~}

|\test{\pgfkeysvalueof{/aaa}}|% typesets | | but slightly wider
\end{document}

上述代码片段的输出

相关内容