在 \csname\endcsname 中创建具有格式化名称的命令

在 \csname\endcsname 中创建具有格式化名称的命令

我正在尝试创建一种方法MakeCommands,该方法将根据作为参数传递的基本名称生成几个命令。但是,我希望允许用户传递带空格的名称,因此必须从传递的参数中提取该名称以用作实际函数名称

我当前拥有的(为了简单起见,只创建一个命令)是:

\newcommand*{\MakeCommand}[1]
{
    \expandafter\newcommand\csname the#1\endcsname{TEST}
}

这是可行的,因为诸如\MakeCommand{Bday}创建命令的调用\theBday会扩展为TEST。但是,如果参数实际上是多个单词(\MakeCommand{Date of Birth}),则必须使用 来调用创建的命令\csname theDate of Birth\endcsname,这并不优雅。

因此我尝试了以下方法:

% \usepackage{xstring}
\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}{\StrSubstitute{#1}{ }{}}
    \expandafter\newcommand\csname the\temp\endcsname{TEST}
}

基本上,使用xstringStrSubstitute命令从传递的参数中提取空格并将其存储在 中\temp。然后使用这个无空格版本的参数作为命令名称。

然而,这不起作用,我得到了多个错误。例如,对于以下 MWE:

\documentclass[a4paper,10pt]{article}
\usepackage{xstring}

\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}{\StrSubstitute{#1}{ }{}}
    \expandafter\newcommand\csname the\temp \endcsname{TEST}
    %\expandafter\newcommand\csname the#1\endcsname{TEST}
}
\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}

我收到以下错误:

Missing \endcsname inserted. \MakeCommand{Date of Birth}
Command \the already defined. \MakeCommand{Date of Birth}
Extra \endcsname. \MakeCommand{Date of Birth}
Missing \begin{document}. \MakeCommand{Date of Birth}
Undefined control sequence. ^^I\theDateofBirth

如果我注释掉该\expandafter...行并\theDateofBirth用 替换文档内容\temp,则文件可以正常编译并输出“DateofBirth”,正如预期的那样。

我可以从其他读数中看出([A][B][C]) 这可能需要额外的\expandafters,但我无论如何也想不出把它们放在哪里以及为什么/如何。


这是一个将显示多个数据点的文件。MakeCommand实际上应该创建三个相关命令。我打算做的是

\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}
    {
        % store spaceless version of parameter
        \StrSubstitute{#1}{ }{}
    }
    \expandafter\newcommand\csname the\temp\endcsname{}% empty variable for use further ahead
    \expandafter\newcommand\csname \temp\endcsname[1]
    {
        % store value of data point
        \expandafter\renewcommand\csname the\temp \endcsname{#1}
    }
    \expandafter\newcommand\csname print\temp\endcsname
    {
        % print out formatted data point
        \textbf{\temp:} \csname\the\temp\endcsname
    }
}

因此,MakeCommands{Date of Birth}将创建三个命令

  • \theDateofBirth,最初是空的
  • \DateofBirth,用户稍后可以调用它来定义\theDateofBirth
  • \printDateofBirth,打印出数据点

例如,\DateofBirth{August 31, 2016}会导致\printDateofBirth输出“出生日期:2016年8月31日”。

答案1

如果您想要删除的只是空格,那么核函数\zap@space就是您所需要的:

\documentclass[a4paper,10pt]{article}

\makeatletter
\newcommand*{\MakeCommand}[1]{%
  \expandafter\newcommand\csname the\zap@space #1 \@empty\endcsname
}
\makeatother
\MakeCommand{Date of Birth}{TEST}

\begin{document}

\theDateofBirth

\end{document}

我已经改变了语法以\MakeCommand允许指定替换文本,否则它将毫无用处。

但是,这个the前缀的选择确实不太好。

(较慢的) 实现如下\StrSubstitute

\documentclass[a4paper,10pt]{article}
\usepackage{xstring}

\newcommand*{\MakeCommand}[1]{%
  \StrSubstitute{#1}{ }{}[\temp]%
  \expandafter\newcommand\csname the\temp\endcsname
}

\MakeCommand{Date of Birth}{TEST}

\begin{document}

\theDateofBirth

\end{document}

答案2

这是一个利用可扩展替换的解决方案,与这个答案。这增加了一整套可扩展的替换系统,因此它不只是关于空格。一旦你这样做了

\setfreplace{removespaces}{ }{}

你可以说\freplace{removespaces}{Date of Birth},它会把每个空格替换为空(即删除空格)DateofBirth 可扩展

尽管您可能不应该\the...在名称中使用,因为该命名约定在 LaTeX 中用于其他事物。可能是\TheDateofBirth\showDateofBirth\printDateofBirth

这是一个 expl3 解决方案

\documentclass{scrartcl}
\usepackage{xparse,etoolbox}

\ExplSyntaxOn

\cs_generate_variant:Nn \cs_generate_variant:Nn { c }

\NewDocumentCommand \setfreplace { m +m +m }
 {
  \freplace_set:nnn { #1 } { #2 } { #3 }
 }
\DeclareExpandableDocumentCommand \freplace { +m +m }
 {
  \freplace:nn { #1 } { #2 }
 }
\quark_new:N \q_freplace
\cs_new:Npn \freplace:nn #1 #2
 {
  \exp_not:f { \use:c { freplace_#1:n } { #2 } }
 }
\cs_new_protected:Npn \freplace_set:nnn #1 #2 #3
 {
  \cs_set:cpx { freplace_#1:n } ##1
   {
    \exp_not:c { freplace_#1_auxi:nw } { } ##1 { \exp_not:N \q_freplace }
   }
  \cs_set:cpx { freplace_#1_auxi:nw } ##1 ##2 ##
   {
    \exp_not:c { freplace_#1_nobraces:nfn }
     { ##1 } { \exp_not:c { freplace_#1_do:n } { ##2 } }
   }
  \cs_set:cpx { freplace_#1_nobraces:nnn } ##1 ##2
   {
    \exp_not:c { freplace_#1_auxii:nn } { ##1 ##2 }
   }
  \cs_generate_variant:cn { freplace_#1_nobraces:nnn } { nf }
  \cs_set:cpx { freplace_#1_auxii:nn } ##1 ##2
   {
    \exp_not:N \str_if_eq:nnTF { \exp_not:N \q_freplace } { ##2 }
     { \exp_stop_f: ##1 }
     {
      \exp_not:c { freplace_#1_addbraces:nfw }
       { ##1 } { \exp_not:c { freplace_#1:n } { ##2 } }
     }
   }
  \cs_set:cpx { freplace_#1_addbraces:nnw } ##1 ##2
   {
    \exp_not:c { freplace_#1_auxi:nw } { ##1 { ##2 } }
   }
  \cs_generate_variant:cn { freplace_#1_addbraces:nnw } { nf }
  \cs_set:cpx { freplace_#1_do:n } ##1
   {
    \exp_not:N \tl_if_empty:nTF { ##1 }
     { \exp_stop_f: }
     {
      \exp_not:c { freplace_#1_auxiii:nww }
       { } ##1 \exp_not:n { #2 \q_freplace \q_stop }
     }
   }
  \cs_set:cpx { freplace_#1_auxiii:nww } ##1 ##2 #2 ##3 \q_stop
   {
    \exp_not:N \str_if_eq:nnTF { \exp_not:N \q_freplace } { ##3 }
     { \exp_stop_f: ##1 ##2 }
     {
      \exp_not:c { freplace_#1_auxiii:nww }
       { ##1 ##2 \exp_not:n { #3 } } ##3 \exp_not:N \q_stop
     }
   }
 }

\cs_generate_variant:Nn \freplace:nn { nV }
\cs_new_protected:Npn \tl_replace_nested:Nnn #1 #2 #3
 {
  \freplace_set:nnn { _tmp_ } { #2 } { #3 }
  \tl_set:Nx #1 { \freplace:nV { _tmp_ } #1 }
 }

\ExplSyntaxOff

\setfreplace{removespaces}{ }{}

\newcommand*{\MakeCommand}[1]
{\expandafter\newcommand\csname the\freplace{removespaces}{#1}\endcsname{TEST}}

\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}

更新后,这是完整的命令(我使用来自的命令etoolbox来简化代码,该命令在帐户中有扩展)

\newcommand*\temp{}
\newcommand*\MakeCommand[1]
 {\edef\temp{\freplace{removespaces}{#1}}%
  \csdef{the\temp}{}%
  \csedef{\temp}##1{\csdef{the\temp}{##1}}%
  \csedef{print\temp}{\unexpanded{\textbf{#1:}} \noexpand\csuse{the\temp}}}

这里还有另一种解决方案,稍微不那么强大,没有 expl3(以防你害怕它并且因为某种原因不想使用它)

\documentclass{scrartcl}
\usepackage{etoolbox}

\long\def\usefirst#1#2{#1}
\long\def\usesecond#1#2{#2}
\def\strcmp#1#2{\ifnum\pdfstrcmp{\unexpanded{#1}}{\unexpanded{#2}}=0 \expandafter\usefirst\else\expandafter\usesecond\fi}
\def\strempty#1{\if\relax\detokenize{#1}\relax\expandafter\usefirst\else\expandafter\usesecond\fi}

\def\endreplace{\endreplace}
\def\nestedreplace#1{\csuse{nestedreplace:#1}}
\def\newnestedreplace#1#2#3{%
  \csdef{nestedreplace:#1}##1{\csuse{nestedreplace:#1:A}##1{\endreplace}}%
  \csdef{nestedreplace:#1:A}##1##{\csuse{doreplacement:#1}{##1}\csuse{nestedreplace:#1:B}}%
  \csdef{nestedreplace:#1:B}##1{\strcmp{##1}{\endreplace}{}
    {{\csuse{nestedreplace:#1}{##1}}\csuse{nestedreplace:#1:A}}}%
  \csdef{doreplacement:#1}##1{\strempty{#1}{}{\csuse{doreplacement:#1:A}{}##1#2\endreplace\qend}}%
  \csdef{doreplacement:#1:A}##1##2#2##3\qend{\strcmp{\endreplace}{##3}
    {\unexpanded{##1##2}}{\csuse{doreplacement:#1:A}{##1##2#3}##3\qend}}%
}

\newnestedreplace{removespaces}{ }{}

\newcommand*{\MakeCommand}[1]
{\expandafter\newcommand\csname the\nestedreplace{removespaces}{#1}\endcsname{TEST}}

\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}

相关内容