将宏名称转换为 pgfkey 名称

将宏名称转换为 pgfkey 名称

让我们考虑一个形式为 的宏名\first@second。我想编写一个命令,将宏的名称转换为pgfkey形式的名称/first/second,以便以下方法等效且具有相同的效果:

% This can change:
\newcommand{\macrotokey}[1]{% WHAT TO DO HERE? %}
\newcommand{\setviakey}[2]{\pgfkeys{#1 = #2}}
\newcommand{\setviamacro}[2]{\pgfkeys{\macrotokey{#1} = #2}}

% If possible I would like to have this syntax on the user side:
\setviakey{/A/B}{Hello}
\setviamacro{\A@B}{Hello}

我不知道从哪里开始做这样的技巧。

答案1

\macrotokey如果您希望在的参数中使用\pgfkeys,那么\macrotokey需要完全扩展。

Expl3 附带一个完全可扩展的例程\cs_to_str:N,其中通过触发两个扩展步骤来传递不带前导转义字符(通常是反斜杠)的控制序列标记的名称\cs_to_str:N。使用此例程,您无需担心诸如值为 32 或负值之类的边缘情况。(32 表示通过诸如或 之类\escapechar的东西传递空格而不是反斜杠作为转义字符。负值表示根本不传递任何转义字符。)\string\detokenize

因此,让我们使用 expl3 来定义一个宏,该宏触发两个扩展步骤,然后启动一个递归循环,检查参数(仍然)是否包含类别 12(其他),如果是,则在再次调用自身之前用 替换其中的第一个,如果不是,则传递参数。\macrotokey{⟨argument⟩}\cs_to_str:N ⟨argument⟩\__MyStuff_macrotokeyReplaceAtLoop:n@@/

我可能会用\cstokentokey而不是来命名这个事物,\macrotokey因为构成 参数的标记的含义\macrotokey并不相关(除非它不应该是\outer标记)——参数是控制序列标记就足够了。它甚至不需要定义。

如果取消注释\exp_end:\exp:w,则的结果\macrotokey 是通过触发恰好两个扩展步骤而获得的\macrotokey
但对于触发形成密钥本身的标记的扩展\pgfkeys而言,这并不是必需的。\pgfkeys

\documentclass{article}
\usepackage{pgfkeys}

\ExplSyntaxOn
\cs_new:Npn \MyStuff_GobbleTillAt:w #1@ {}
\cs_new:Npn \MyStuff_ReplaceAt:w #1@ { #1/ }
\cs_new:Nn \__MyStuff_macrotokeyReplaceAtLoop:n 
  {
    \tl_if_empty:oTF {\MyStuff_GobbleTillAt:w #1 @} 
                     { %\exp_end: 
                       #1
                       % If the name of the control-sequence-token begins with @, then it denotes an absolute path,
                       % otherwise the path is relative to what \pgfkeysdefaultpath was defined to via the /.cd-handler.
                       % If you do /#1  instead of #1, then every path is absolute and the names of
                       % control-sequence-tokens shall not begin with `@`.
                       % If you do  \pgfkeysdefaultpath #1  instead of #1, then every path is relative and the names of
                       % control-sequence-tokenss shall not begin with `@`.
                     } 
                     {
                       \exp_args:No \__MyStuff_macrotokeyReplaceAtLoop:n 
                                    {\MyStuff_ReplaceAt:w #1}
                     }
  }
\cs_new:Npn \macrotokey #1 
  { %\exp:w 
    \exp_args:NNo \exp_args:No 
                  \__MyStuff_macrotokeyReplaceAtLoop:n
                  { \cs_to_str:N #1 } 
  }
\ExplSyntaxOff

\newcommand{\setviamacro}[2]{\pgfkeys{\macrotokey{#1}={#2}}}
\newcommand{\setviakey}[2]{\pgfkeys{#1={#2}}}

\makeatletter

\pgfkeys{%
  %---------------------------------------------------------------
  % define the code to execute when encountering the key /A/B/C:
  %---------------------------------------------------------------
  /A/B/C/.code=\message{^^JKey /A/B/C's value in parentheses is: (#1)^^J},
  %---------------------------------------------------------------
  % Use the key /A/B/C :
  %---------------------------------------------------------------
  /A/B/C=Test 1: Absolute key provided,
  \macrotokey{\@A@B@C} = Test 1: Control sequence token denoting absolute key provided,
  /A/.cd,
  B/C=Test 2: Relative key provided,
  \macrotokey{\B@C} = Test 2: Control sequence token denoting relative key provided,
  /A/B/.cd,
  C=Test 3: Relative key provided,
  \macrotokey{\C} = Test 3: Control sequence token denoting relative key provided,
}

\setviakey{/A/B/C}{Test 4: Absolute key provided}
\setviamacro{\@A@B@C}{Test 4: Control sequence token denoting absolute key provided}

\stop

以下消息传送到控制台:

Key /A/B/C's value in parentheses is: (Test 1: Absolute key provided)


Key /A/B/C's value in parentheses is: (Test 1: Control sequence token denoting 
absolute key provided)

Key /A/B/C's value in parentheses is: (Test 2: Relative key provided)


Key /A/B/C's value in parentheses is: (Test 2: Control sequence token denoting 
relative key provided)

Key /A/B/C's value in parentheses is: (Test 3: Relative key provided)


Key /A/B/C's value in parentheses is: (Test 3: Control sequence token denoting 
relative key provided)

Key /A/B/C's value in parentheses is: (Test 4: Absolute key provided)


Key /A/B/C's value in parentheses is: (Test 4: Control sequence token denoting 
absolute key provided)

答案2

这可能不是最强大的版本。

所有宏的名称中都不使用,@因为这样会导致@11@catcode 为 11,即宏名称中的 catcode)和@12(由 生成的 catcode \string)之间发生冲突。

有办法解决这个问题 - 甚至最近有一个关于的问题,但我现在找不到 - 但老实说,无论如何我都不会那样使用它。


您可以只定义\A@B扩展/A/B并将其用作,\setviakey{\A@B}{…}但我猜\A@B在您的用例中它还有另一个用途。

代码

\documentclass{article}
\usepackage{pgfkeys}
\pgfkeys{/A/B/.code=``#1''}
\begin{document}
\def\gobble#1{}

\def\macrotokeyatstripperstop{!stop!}%
\newcommand*\macrotokey[1]{%
  /\expandafter\expandafter\expandafter\macrotokeyatstripper
  \expandafter\gobble\string#1@\macrotokeyatstripperstop
}%
\def\macrotokeyatstripper#1@#2{%
  #1\ifx\macrotokeyatstripperstop#2\expandafter\gobble\else
    /\expandafter\macrotokeyatstripper
  \fi#2}%

\newcommand{\setviakey}[2]{\pgfkeys{#1 = #2}}
\newcommand{\setviamacro}[2]{\pgfkeys{\macrotokey{#1} = #2}}

\setviakey{/A/B}{Hello}

\makeatletter
\setviamacro{\A@B}{Hello World}
\makeatother
\end{document}

相关内容