
Ich möchte einen TeX-Befehl zum „Makro-Erstellen“ (nennen wir ihn \buildmacro
) definieren, der als Argument eine Zeichenfolge „normaler“ Zeichen (z. B. Title
) verwendet und Folgendes erfüllt:
- Der Befehl
\buildmacro{Title}
definiert den Zähler mit dem NamenTitleXcounter
und initialisiert ihn auf 0. Wenn Sie an einer beliebigen Stelle in der Datei den TeX-Befehl eingeben
\Title{<arg>}
, hat dies zur Folge, dass der Zähler erhöhtTitleXcounter
und der Wert dann<arg>
in ein neues TeX-Makro mit dem Namen eingefügt wird\Title-<x>
, wobei<x>
der aktuelle Wert von istTitleXcounter
;hier
<arg>
ist ein Stück TeX-Code (der Text, Mathematik, Absätze oder andere TeX-Befehle enthalten kann).
Betrachten Sie das folgende Minimalbeispiel:
\documentclass{article}
\usepackage{ifthen}
\def\buildmacro#1{...}% the definition of \buildmacro
\begin{document}
\buildmacro{Title}
% 'Read' several titles
\Title{The first title}
\Title{The second title}
\Title{The third title}
\newcount\tempcount
\tempcount=1\relax
% 'List' all the Titles
\whiledo{\the\tempcount<\numexpr \thetitleXcounter+1\relax}
{%
\textsc{\csname title-\the\tempcount\endcsname}\par
\advance\tempcount by 1\relax
}
\end{document}
Ich habe mit \csname
und \endcsname
, \expandafter
, \def
, \gdef
und den \long
Versionen ‚gespielt‘, aber ohne ‚Glück‘.
Herausgegeben(um einige der Kommentare zu beantworten):
Der Zweck besteht darin, einen einfachen Mechanismus zum Erstellen einer Reihe indexierter Befehle mit demselben Stammnamen zu haben (ähnlich einer Befehlsliste). Man kann die \csname <...> \endcsname
Konstruktion immer verwenden, um die Befehle auszugeben.
Antwort1
Aus der Beschreibung des erforderlichen Ablaufs scheint dies recht einfach zu sein
\newcommand\buildmacro[1]{%
% Create a new counter named after the argument
\newcounter{#1Xcounter}%
% Create a new command named after the argument,
% itself taking one argument
\expandafter\newcommand\csname #1\endcsname[1]{%
% Step the counter
\stepcounter(#1Xcounter)%
% Store the argument
\expandafter\newcommand\csname #1-\arabic{#1Xcounter}\endcsname
{##1}%
}%
}
Da auch eine einfache Version gewünscht wurde
\long\def\buildmacro#1{%
\csname newcount\expandafter\endcsname\csname #1Xcounter\endcsname
\long\expandafter\def\csname #1\endcsname##1{%
\global\expandafter\advance\csname #1Xcounter\endcsname by 1
\expandafter\def\csname #1-\expandafter\number\csname #1Xcounter\endcsname
\endcsname
{##1}%
}%
}
Antwort2
Wie ich in einem Kommentar erwähnt habe, können beim Erstellen von Makronamen weder Bindestriche noch Ziffern verwendet werden, es sei denn, Sie schreiben immer das Äquivalent \csname ... \endcsname
oder stellen sicher, dass die Catcodes bei ihrer Verwendung geändert werden.
Daher ist es notwendig, die vorgeschlagene Benutzeroberfläche zu ändern.
Die Grundidee scheint darin zu bestehen, eine Reihe indizierter Argumentlisten zu erstellen, die abgerufen werden können, indem der Name der Liste und der Index des Elements innerhalb dieser Liste angegeben werden.
Dies könnte mit kommagetrennten Listen umgesetzt werden. Hier ist eine vorgeschlagene Schnittstelle, die nur expl3
Syntax verwendet, die von den Entwicklern als stabil angesehen wird:
\buildmacro{<list identifier>}
\addmacro{<list identifier>}{<item to be added>}
\fetchmacro{<list identifier>}{<index of item to be fetched>}
\listmacro{<list identifier>}
Das erste richtet alles ein, indem es eine neue Liste initialisiert. Das zweite fügt ein Element zu einer Liste hinzu. Das dritte ruft ein Element aus einer Liste ab. Das vierte listet alle Elemente einer Liste der Reihe nach auf.
Proof of Concept (wenn auch kaum originell):
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected_nopar:Nn \digitalink_buildmacro:n
{
\clist_new:c { g_digitalink_#1_clist }
}
\NewDocumentCommand \buildmacro { m }
{
\digitalink_buildmacro:n { #1 }
}
\NewDocumentCommand \addmacro { m +m }
{
\clist_gput_right:cn { g_digitalink_#1_clist }
{
#2
}
}
\NewDocumentCommand \fetchmacro { m m }
{
\clist_item:cn { g_digitalink_#1_clist } { #2 }
}
\NewDocumentCommand \listmacro { m }
{
\clist_use:cn { g_digitalink_#1_clist } { \par }
}
\ExplSyntaxOff
\begin{document}
\buildmacro{dititle}
\addmacro{dititle}{Here's some text.}
\addmacro{dititle}{Here's some more.}
\fetchmacro{dititle}{1}
\fetchmacro{dititle}{2}
\fetchmacro{dititle}{1}
List:
\listmacro{dititle}
\end{document}
Antwort3
Eine Version, \@namedef
die \@nameuse
„Makro“-Namen vortäuscht, was ohne Catcode-Änderung nicht möglich wäre.
Die Zuweisung \newcounter
innerhalb von Makros kann zu Problemen mit Zählerregistern führen.
\documentclass{article}
\makeatletter
\newcommand{\buildmacro}[1]{%
\newcounter{#1Xcounter}%
\expandafter\newcommand\csname #1\endcsname[1]{%
\stepcounter{#1Xcounter}%
\@namedef{#1-\number\value{#1Xcounter}}{##1}%
}
}
\newcommand{\displaymacrocontent}[2]{%
\@nameuse{#1-#2}%
}
\makeatother
\begin{document}
\buildmacro{Title}
\Title{The first title}
\Title{The second title}
\Title{The third title}
\newcount\tmpcntr
\loop\unless\ifnum\tmpcntr > 4
\advance\tmpcntr by 1
\displaymacrocontent{Title}{\number\tmpcntr}
\repeat
\end{document}
Antwort4
Der Effekt, den Sie in Ihrem Pseudocode zeigen, lässt sich viel einfacher mit einem Listenmakro erzielen, das die Ausführung einer Schleife ermöglicht, ohne dass man wissen muss, wie viele Objekte sich darin befinden, und ohne Zähler weiterzuschalten.
\documentclass{article}
\makeatletter
\newcommand{\Titlecontainer}{\@empty}% initialize
\newcommand{\Title}[1]{%
\expandafter\def\expandafter\Titlecontainer\expandafter{%
\Titlecontainer\LISTSEP{#1}%
}%
}
\makeatother
\begin{document}
\Title{The first title}
\Title{The second title}
\Title{The third title}
% 'List' all the Titles
\begingroup % keep the change to \LISTSEP local
\def\LISTSEP#1{\textsc{#1}\par}
\Titlecontainer
\endgroup
\end{document}
Eine „abstrakte“ Version:
\documentclass{article}
\makeatletter
\newcommand{\Titlecontainer}{\@empty}% initialize
\newcommand{\Title}[1]{%
\expandafter\def\expandafter\Titlecontainer\expandafter{%
\Titlecontainer\LISTSEP{#1}%
}%
}
\newcommand{\listmap}[2]{% #1 is the list name, #2 the code
\begingroup\def\LISTSEP##1{#2}#1\endgroup
}
\makeatother
\begin{document}
\Title{The first title}
\Title{The second title}
\Title{The third title}
% 'List' all the Titles
\listmap{\Titlecontainer}{\textsc{#1}\par}
\end{document}
Noch einfacher mit expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\Title}{m}
{
\seq_put_right:Nn \digitalink_titles_seq { #1 }
}
\NewDocumentCommand{\ProcessTitles}{+m}
{
\seq_map_inline:Nn \digitalink_titles_seq { #1 }
}
\seq_new:N \digitalink_titles_seq
\ExplSyntaxOff
\begin{document}
\Title{The first title}
\Title{The second title}
\Title{The third title}
\ProcessTitles{\textsc{#1}\par}
\newcounter{titlecount}
\ProcessTitles{\stepcounter{titlecount}\arabic{titlecount} -- #1\par}
\end{document}
Abstrakte Versionen
Klassische Methode
\documentclass{article}
\makeatletter
\newcommand{\definelist}[1]{%
\@namedef{#1}##1{%
\expandafter\def\csname #1@container\expandafter\expandafter\expandafter\endcsname
\expandafter\expandafter\expandafter{%
\csname #1@container\endcsname\LISTSEP{##1}%
}%
}%
\@namedef{#1@container}{\@empty}% initialize
}
\newcommand{\maplist}[2]{%
\begingroup
\def\LISTSEP##1{#2}%
\@nameuse{#1@container}%
\endgroup
}
\makeatother
\definelist{Title}
\begin{document}
\Title{The first title}
\Title{The second title}
\Title{The third title}
% 'List' all the Titles
\maplist{Title}{\textsc{#1}\par}
\newcounter{tempcount}
\maplist{Title}{\stepcounter{tempcount}\arabic{tempcount} -- #1\par}
\end{document}
Mitexpl3
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\definelist}{m}
{
\seq_new:c { l_digitalink_list_#1_seq }
\cs_new_protected:cpn { #1 } ##1
{
\seq_put_right:cn { l_digitalink_list_#1_seq } { ##1 }
}
}
\NewDocumentCommand{\maplist}{m +m}
{
\seq_map_inline:cn { l_digitalink_list_#1_seq } { #2 }
}
\ExplSyntaxOff
\definelist{Title}
\begin{document}
\Title{The first title}
\Title{The second title}
\Title{The third title}
% 'List' all the Titles
\maplist{Title}{\textsc{#1}\par}
\newcounter{tempcount}
\maplist{Title}{\stepcounter{tempcount}\arabic{tempcount} -- #1\par}
\end{document}