下面的宏\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}