我已经看过这些帖子了宏定义宏和
定义其他宏的宏,这些宏也定义其他宏但不幸的是,我没能弄清楚如何纠正这个问题。我想创建一个宏,它将在@
宏名前面生成宏 + 全局宏,如下例所示:
\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
我已经检查Alice
并Bob
使用\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
我们就需要摆脱空间。
请注意,使用\edef
in\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}
如果您提供更多有关您在实践中尝试实现的目标的信息,那么可以开发一个可以更好地进行错误管理的界面。