将 TikZ 样式传递给宏创建的宏时出现扩展问题

将 TikZ 样式传递给宏创建的宏时出现扩展问题

我(再次)在扩展方面遇到困难。这次我有一个通用函数\misdirection,可以根据一些选项创建一个 TikZ 环境。然后,我创建一个宏\freezeStyle,该宏创建一个新宏,该宏已将一些选项冻结为\misdirection。因此\freezeStyle接收了我最终想要传递给的样式\misdirection

我尝试了几次\edef,但无法让它工作,所以我选择将代码置于一种状态,以演示编译但具有不良行为的情况。这次尝试的灵感来自如何使用 pgfkeys 提交一组 tikz 命令?

那么:我应该如何修改它以使其按预期工作?请注意,我的实际误导调用将涉及\csname,因此,我希望 expandafter 解决方案也能展示如何使您的解决方案适应这种情况(有关详细信息,请参阅代码)。

为了交叉引用,这里有一个类似的问题:编写宏的宏和意外的副作用,具有相同类型的不良行为,但解决方案与定义定制的有关\csname,这并不适用于这个问题。

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{
\begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
\end{tikzpicture}
}

\pgfkeys{
    /tikz/.cd,
        execute style/.style = {#1},
        execute macro/.style = {execute style/.expand once=#1},
    /demo/.cd,
        name/.store in=\savedname,
        style/.store in=\savedstyle
}

\newcommand{\freezeStyle}[1][]{%
    \pgfqkeys{/demo}{#1}
    \expandafter\def\csname\savedname\endcsname##1{%
        % Incorrect because \savedstyle is not expanded until runtime.
        % I want the value of \savedstyle hardcoded into this macro.
        \misdirection[execute macro = \savedstyle]{##1}

        % This doesn't work either...
        %\expandafter\misdirection\expandafter[\savedstyle]{##1}

        % In my real code, the misdirection call looks more like:
        %\csname someArgumentDependentStuff \endcsname[put style here]{args}
    }
}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4} % Undesirably, this is green with a big arrow.
    \helloDad{5}
\end{document}

答案1

我只需使用“辅助”宏,以便\savedstyle可以扩展(一次):

\documentclass[varwidth,border=5]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw [blue,#1] (tree) -- (apple);
  \end{tikzpicture}\ignorespaces% or \par?
}

\pgfkeys{demo/.cd,
  name/.store in=\savedname,
  style/.store in=\savedstyle
}

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\FreezeStyle\expandafter{\savedstyle}{\savedname}}

\def\FreezeStyle#1#2{%
  \expandafter\def\csname#2\endcsname##1{%
     \misdirection[#1]{##1}}}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4} 

    \helloDad{5}
\end{document}

注意,我\ignorespaces在命令中添加了一个\misdirection

在此处输入图片描述

答案2

有了expl3,只是为了好玩:)

\documentclass{article}
\usepackage{xparse} % automatically loads expl3
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
  \end{tikzpicture}}

\ExplSyntaxOn

% Declare our variables; it's good practice :)
\tl_new:N \l_demo_name_tl
\tl_new:N \l_demo_style_tl

% Set up our keys (`tl` is short for `token list`)
\keys_define:nn { demo } {
  name  .tl_set:N = \l_demo_name_tl,
  style .tl_set:N = \l_demo_style_tl,
}

% Create a macro to behave as if we would call
% \demo_freeze_style:Nn \helloMom { red, thick }...
\cs_new:Nn \demo_freeze_style:Nn {
  \cs_new:Npn #1 ##1 {
    \misdirection[#2]{##1}
  }
}
% ...but then add support for saner syntax in our use case
\cs_generate_variant:Nn \demo_freeze_style:Nn { cV }

% Now, set our keys and use our command :)
\NewDocumentCommand \freezeStyle { O{} } {
  \group_begin:
  \keys_set:nn { demo } { #1 }
  \demo_freeze_style:cV { \tl_use:N \l_demo_name_tl } \l_demo_style_tl
  \group_end:
}

\ExplSyntaxOff


\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4}
    \helloDad{5}
\end{document}

输出

答案3

不使用 的方法如下expl3

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\edef\csname\savedname\endcsname##1{%
    \noexpand\misdirection[\savedstyle]{##1}}}

注意\edef这里使用了,\noexpand用于保护不应扩展的宏。

\typeout{\meaning\helloMom}
macro:#1->\misdirection [execute macro=red,thick]{#1}

输出

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\pgfkeys{
    /demo/.cd,
        name/.store in=\savedname,
        style/.store in=\savedstyle
}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
  \end{tikzpicture}}

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\edef\csname\savedname\endcsname##1{%
    \noexpand\misdirection[\savedstyle]{##1}}}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4}
    \helloDad{5}
\end{document}

相关内容