宏定义宏定义全局宏(类似于`\author`或`\title`字段)

宏定义宏定义全局宏(类似于`\author`或`\title`字段)

我已经看过这些帖子了宏定义宏
定义其他宏的宏,这些宏也定义其他宏但不幸的是,我没能弄清楚如何纠正这个问题。我想创建一个宏,它将在@宏名前面生成宏 + 全局宏,如下例所示:

 \def\Bob#1{\gdef\@Bob{#1}}
 \Bob{My name is Bob}
 \makeatletter
 \@Bob % it will display `My name is Bob'
 \Bob{Name: Bob}
 \@Bob % it will display `Name: Bob'
 \makeatother

所以这是可行的。但是:

 \def\DefineField#1{%
     \expandafter\edef\csname #1\endcsname##1{%
         \gdef\csname @#1 \endcsname{##1}%
     }%
 }
 \DefineField{Alice}
 \Alice{My name is Alice}

不起作用。我得到了Undefined control sequence.\@Alice我已经检查AliceBob使用\show命令:

> \Bob=macro:
#1->\gdef \@Bob {#1}.
l.20 \show\Bob
> \Alice=macro:
#1->\gdef \@Alice  {#1}.
l.21 \show\Alice

它们的结构完全相同。我错过了什么?MWE:

\documentclass[preview]{standalone}
\makeatletter

\def\DefineField#1{%
    \expandafter\edef\csname #1\endcsname##1{%
        \gdef\csname @#1 \endcsname{##1}%
    }%
}
\DefineField{Alice}
\Alice{My name is Alice}


\def\Bob#1{\gdef\@Bob{#1}}
\Bob{My name is Bob}

\begin{document}
%\@Alice  % Undefined control sequence.
\@Bob
\Bob{No my name is Alice}

\@Bob
\show\Bob
\show\Alice
\makeatother
\end{document}

答案1

的定义\DefineField中有多余的空格\gdef\csname @#1 \endcsname{##1}%,应该是

\def\DefineField#1{%
  \expandafter\edef\csname #1\endcsname##1{%
    \gdef\csname @#1\endcsname{##1}%
  }%
}

使用空格,您可以定义命令\@Alice␣(其中代表空格),可以通过检查\@Alice␣定义来验证

\expandafter\show\csname @Alice \endcsname

如果我们想要定义,\@Alice我们就需要摆脱空间。


请注意,使用\edefin\DefineField可能会产生意想不到的后果,因此您可能需要考虑采用马丁·沙雷尔回答或者埃格尔回答

答案2

我不会\edef在这里使用,但会与第二个人合作\expandafter

这里的主要问题是#1和之间有一个空格\endcsname,所以你实际上定义了\@Alice<space>,这可以通过\csname.

正确的代码是:

\def\DefineField#1{%
    \expandafter\def\csname #1\endcsname##1{%
        \expandafter\gdef\csname @#1\endcsname{##1}%
    }%
}

答案3

您正在定义\csname @Alice \endcsname会导致控制序列标记的名称中带有尾随空格。后面的空格在\csname标记化过程中找不到,因为它跟在控制字后面,但#1和之间的空格\endcsname不会被删除。

你的使用\edef也很有问题:如果你错误地使用了\DefineField{Alice}两次,

\DefineField{Alice}
\Alice{My name is Alice}
\show\Alice

\DefineField{Alice}
\show\Alice

你会得到

> \Alice=macro:
#1->\gdef \@Alice {#1}.

> \Alice=macro:
#1->\gdef My name is Alice{#1}.

\Alice可以通过首先检查是否定义来解决这个问题:

\def\DefineField#1{%
  \@ifundefined{#1}
    {% go on, it's undefined
      \expandafter\edef\csname #1\endcsname##1{%
        \gdef\csname @#1\endcsname{##1}%
      }%
    }
    {% ignore the redefinition
      \PackageWarning{andywiecko}{%
        \expandafter\noexpand\csname #1\endcsname already defined%
      }%
    }%
}

xparse使用 和的更简单方法expl3

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\DefineField}{m}
 {
  \cs_new_protected:cpn { #1 } ##1
   {
    \cs_new_protected:cpn { @#1 } { ##1 } % or `\cs_new:cpn
   }
 }

\ExplSyntaxOff

\DefineField{Alice}
\Alice{My name is Alice}

\expandafter\show\csname @Alice\endcsname

\DefineField{Alice}如果使用两次或者你尝试执行此操作,则会产生标准错误\DefineField{box};代码将显示

> \@Alice=\protected\long macro:
->My name is Alice.

您可能希望\cs_new:cpn在第二个实例中使用,这取决于您将使用\@Alice宏的扩展上下文。


更合适的expl3接口,\@foo您可以\UseField{foo}在需要时定义宏,而不是定义宏。提供了一种覆盖现有字段的方法。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\prop_new:N \g_andywiecko_fields_prop
\cs_new:Nn \andywiecko_define_field:nn
 {
  \prop_gput:Nnn \g_andywiecko_fields_prop { #1 } { #2 }
 }

\NewDocumentCommand{\DefineField}{sm}
 {
  \cs_set_protected:Nn \__andywiecko:n
   {
    \andywiecko_define_field:nn { #2 } { ##1 }
   }
  \IfBooleanTF { #1 }
   {% override an existing definition
    \cs_gset_eq:cN { #2 } \__andywiecko:n
   }
   {% new field
    \cs_new_eq:cN { #2 } \__andywiecko:n
   }
 }

\NewExpandableDocumentCommand{\UseField}{m}
 {
  \prop_item:Nn \g_andywiecko_fields_prop { #1 }
 }

\ExplSyntaxOff

\DefineField{author} % gives error

\DefineField*{author} % no error, use new interface

\DefineField{Alice}
\Alice{My name is Alice}

\DefineField{Alice} % gives error

答案4

在其他答案中,-expression 内的虚假空间\csname..\endcsname已经被指出。

如果您希望避免输入那么多\csname..\endcsname表达式,我可以提供一个宏\name

\name接受以下未嵌套在括号中的标记作为其第一个参数,接受以下嵌套在括号中的标记作为其第二个参数,并应用于\csname..\endcsname其第二个参数。
构成第一个参数的标记可以是:\global\long\outer\def\newcommand\string\meaning或任何其他。您也可以将第一个参数留空。

例子:

\name\show{foo}产量:\show\foo

\name\def{foo}...产量:\def\foo...

\name\global\long\outer\def{foo}...产量:\global\long\outer\def\foo...

\name\newcommand{foo}...产量:\newcommand\foo...

\name{foo}...产量:\foo

\name\name\global\let{foo}={bar}
产量:
\name\global\let\foo={bar}
产量:
\global\let\foo=\bar

\documentclass{article}

\newcommand\UDExchange[2]{#2#1}
\newcommand\name{}\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{%
  \expandafter\UDExchange\expandafter{\csname#2\endcsname}{ #1}%
}%

\makeatletter
\newcommand\novalueprovided{\nfss@text{\reset@font\bfseries??}}
\makeatother

\newcommand\DefineField[2][\novalueprovided]{%
  \name\newcommand{#2}[1]{\name\gdef{@#2}{##1}}%
  \name\newcommand{@#2}{}%
  \name{#2}{#1}%
}%

\newcommand\UseField[1]{\name{@#1}}%

\DefineField[initial value of field Alice]{Alice}

\DefineField{Bob}

\begin{document}

{\tt\frenchspacing
\name\string{Alice}: \name\meaning{Alice}

\name\string{@Alice}: \name\meaning{@Alice}

\name\string{Bob}: \name\meaning{Bob}

\name\string{@Bob}: \name\meaning{@Bob}

\verb|\UseField{Alice}:| \UseField{Alice}

\verb|\UseField{Bob}:| \UseField{Bob}
}

\hrulefill

Now the fields are changed:

\Alice{My name is Alice}

\Bob{My name is Bob}

{\tt\frenchspacing
\name\string{Alice}: \name\meaning{Alice}

\name\string{@Alice}: \name\meaning{@Alice}

\name\string{Bob}: \name\meaning{Bob}

\name\string{@Bob}: \name\meaning{@Bob}

\verb|\UseField{Alice}:| \UseField{Alice}

\verb|\UseField{Bob}:| \UseField{Bob}
}

\end{document}

如果您提供更多有关您在实践中尝试实现的目标的信息,那么可以开发一个可以更好地进行错误管理的界面。

在此处输入图片描述

相关内容