
csname
我正在嘗試定義一個新命令,但使用和“動態”指定新的控製字串endcsname
。(這是為了實現依賴注入模式。) **或者,有沒有辦法做到這一點expl3
?
通常,我一直在使用\expandafter\newcommand
...但在這個用例中,我想定義csname
WITHIN\newcommand
的第一個參數,如下所示:
\newcommand{\expandbefore\csname \GetCommandName \endcsname}[1]{\small I did it!!!}
\newcommand\expandbefore\csname \GetCommandName \endcsname{\small I did it again!!!}
我希望大括號內的所有內容都可以在之前擴展\newcommand
-但不依賴\expandafter
之前\newcommand
。
問題:
我很好奇在 LaTeX 中是否有一種正常的方法可以做到這一點,而不必依賴另一個進程輸入緩衝區 hack(使用 Lua)。
在 s 參數中新增
expandafter
、nameuse
、edef
、let
、csname
等\newcommand
只會導致重新定義這些指令時出錯。 (即使在{}
或begingroup
關閉。試圖
\meaning
\expandafter
弄清楚它是如何工作的失敗了(可以預見,也很有趣)。
答案1
我(稍加修改)引用我的回答對這個問題在空格之後定義一個控制序列因為它似乎也適用於你的問題:
透過套用#{
- 表示法,您可以定義最後一個參數由左大括號分隔的巨集。與收集參數時刪除的其他參數分隔符號不同,TeX 會保留一個分隔左大括號。
(實際上,該機制並不限於開啟大括號字元標記。您可以使用在定義時類別代碼為 1 的任何標記。也可以#\WeIrd
在 之後\let\WeIrd={
。)
分隔參數可以為空。
因此,為了從一組擴展為一組字元令牌的令牌中獲取控制序列令牌,這些字元令牌形成所討論的控制序列令牌的名稱,以便定義和呼叫該控制序列令牌,您可以(透過應用#{
-notation )發明一個單一的控制序列\name
,它處理一個大括號分隔的參數,後面跟著一個未分隔的參數(嵌套在大括號中)。讓 TeX 取得參數後,您可以讓 TeX 旋轉它們並將其應用於\csname..\endcsname
大括號內提供的參數。相關控制序列標記的名稱也可以包含空格標記。
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
\name foo{bar}
→ 擴充步驟 1:
\UD@innername{foo}{bar}
→ 擴充步驟 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ 擴充步驟 3:
\UD@exchange{\bar}{foo}
→ 擴充步驟 4:
foo\bar
。
在擴展上下文中,您將需要四個\expandafter
鏈來獲取結果。
由於\romannumeral
遇到非正數時不會產生任何標記,因此可以添加一些\romannumeral
- 擴展以減少\expandafter
- 鏈的數量。
要么做\romannumeral\name0 foo{bar}
。這樣,只需要一條\expandafter
鏈擊中-token。\romannumeral
或\romannumeral
在定義中對 - 擴展進行“硬編碼”——這樣就\expandafter
需要兩個 - 鏈。第一個用於獲得 的頂級擴展\name
。第二個用於誘導\romannumeral
擴張。
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
使用這樣的宏,您不必受限於特定的定義命令:
\name{foo}
→ \foo
。 (←這是您不定義而只是透過 呼叫/使用控制序列的方式\name
。)
\name\newcommand{foo}
→ \newcommand\foo
。
\name\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
。
\name\global\long\outer\def{foo}
→ \global\long\outer\def\foo
。
\name\expandafter{foo}\bar
→ \expandafter\foo\bar
。
\name\let{foo}=\bar
→ \let\foo=\bar
。
\name\string{foo}
→ \string\foo
。
\name\meaning{foo}
→ \meaning\foo
。
您也可以使用這樣的巨集來定義/呼叫名稱包含空格的巨集:
\name{foo }
→ \foo␣
。
\name\newcommand{foo }
→ \newcommand\foo␣
。
\name\DeclareRobustCommand{foo }
→ \DeclareRobustCommand\foo␣
。
\name\global\long\outer\def{foo }
→ \global\long\outer\def\foo␣
。
\name\expandafter{foo }\bar
→ \expandafter\foo␣\bar
。
\name\let{foo }=\bar
→ \let\foo␣=\bar
。
\name\string{foo }
→ \string\foo␣
。
\name\meaning{foo }
→ \meaning\foo␣
。
在收集相關控制序列令牌的名稱時,\name
將觸發可擴展令牌的擴展:
\def\GetCommandName{FooBar}
\name\newcommand{\GetCommandName}[1]{\small I did it!!!}
→\newcommand\FooBar[1]{\small I did it!!!}
\def\GetCommandName{\CommandNamePartA\CommandNamePartB}
\def\CommandNamePartA{Ba}
\def\CommandNamePartB{r\InnerCommandNamePart o}
\def\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
→\newcommand\BarFoo{\small I did it again!!!}
您也可以將呼叫嵌套到\name
:
範例1:
\name\name\expandafter{f o o }{b a r }
處理第一個\name
產量:
\name\expandafter\f␣o␣o␣{b a r }
。
處理第二個\name
產量:
\expandafter\f␣o␣o␣\b␣a␣r␣
。
(類似地:\name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
。)
範例2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
處理第一個\name
產量:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
。
處理第二個\name
產量:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
。
處理第三個\name
產量:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
範例3:
在擴充上下文中,您可以使用\romannumeral
-expansion 以使事情繼續進行。
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
不斷擴大,直到找到一些數字。最後它會找到數字0
,而非正數\romannumeral
則不會傳遞任何令牌:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
處理第一個\name
產量:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
。
處理第二個\name
產量:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
。
處理第三個\name
產量:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
現在\romannumeral
找到了號碼0
。因此\romannumeral
-expansion 被中止並且\romannumeral
不會傳遞任何 token:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
。
請注意,\name
內部\csname
適用
可擴展令牌的擴展發生在
\csname
其搜尋匹配期間\endcsname
收集形成所討論的控制序列令牌的名稱的字元令牌的過程中。\csname
作為副作用應用,會產生為所討論的控制序列分配\relax
- 原語的含義,以防所討論的控制序列在應用之前未定義\csname
。即使\globaldefs
- 參數在應用時具有正值,該分配也將僅限於當前範圍\csname
。
%%\errorcontextlines=1000
\documentclass[a4paper]{article}
\usepackage{textcomp}%
\parindent=0cm
\parskip=\medskipamount
\makeatletter
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
\name\newcommand{foo}[2]{%
Control sequence whose name does not contain any space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{foo }[2]{%
Control sequence whose name has a trailing space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\newcommand*\GetCommandName{\CommandNamePartA\CommandNamePartB}
\newcommand*\CommandNamePartA{Ba}
\newcommand*\CommandNamePartB{r\InnerCommandNamePart o}
\newcommand*\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
\begin{document}
\name{foo}{Arg 1}{Arg 2}
\name{foo }{Arg 1}{Arg 2}
\name{ f o o }{Arg 1}{Arg 2}
Nesting \texttt{\string\name}:
\name\expandafter\newcommand\expandafter*\expandafter{C o N f u SiO n}\expandafter{%
\romannumeral\name\name\name0 %
\expandafter\expandafter\expandafter{F O O}\expandafter{B A R}{C R A Z Y}%
}%
\texttt{\name\string{C o N f u SiO n} is \name\meaning{C o N f u SiO n}}%
\\
Playing around with expandable tokens:
\texttt{\name\string{\GetCommandName}:}
\texttt{\name\meaning{\GetCommandName}}
\name{\GetCommandName}%
Playing around with grouping:
%Be aware that \texttt itself opens up a new scope for typesetting its argument.
%\globaldefs=1\relax
\texttt{%
\begingroup\name\string{w e i r d } is \name\endgroup\meaning{w e i r d }%
}%
\texttt{%
\name\string{w e i r d } is \name\meaning{w e i r d }%
}%
\end{document}
答案2
LaTeX 已經有一個命令形式,它採用命令的名稱而不是 csname 標記:
\@namedef{\GetCommandName}{\small I did it!!!}
應該要做你想做的,這很簡單\expandafter\def\csname\GetCommandName\endcsname{..}