
Я хотел бы определить команду «макросоздания» TeX (назовем ее \buildmacro
), которая принимает в качестве аргумента строку «обычных» символов (например, Title
) и которая удовлетворяет следующим условиям:
- команда
\buildmacro{Title}
определяет счетчик с именемTitleXcounter
и инициализирует его значением 0. при выполнении в какой-то точке файла команды TeX
\Title{<arg>}
это приведет к увеличению счетчикаTitleXcounter
и последующему помещению значения<arg>
в новый макрос TeX с именем\Title-<x>
, где<x>
- текущее значение valueTitleXcounter
;здесь
<arg>
— фрагмент кода TeX (который может содержать текст, математические выражения, абзацы любых других команд TeX).
Рассмотрим следующий минимальный пример:
\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}
Я «игрался» с версиями \csname
и \endcsname
, \expandafter
, \def
, \gdef
и \long
, но без «удачи».
Отредактировано(чтобы ответить на некоторые комментарии):
Цель этого — иметь простой механизм для создания набора индексированных команд с одинаковым корневым именем (похоже на список команд). Всегда можно использовать конструкцию \csname <...> \endcsname
для выдачи команд.
решение1
Из описания требуемого потока это кажется довольно простым.
\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}%
}%
}
Так как была также запрошена простая версия
\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}%
}%
}
решение2
Как я уже упоминал в комментарии, ни дефис, ни цифра не могут использоваться при построении имен макросов, если только вы не будете всегда писать эквивалент \csname ... \endcsname
или не убедитесь, что коды меняются при их использовании.
Следовательно, необходимо изменить пользовательский интерфейс по сравнению с предложенным.
Основная идея, по-видимому, заключается в создании серии индексированных списков аргументов, которые можно получить, указав имя списка и индекс элемента в этом списке.
Это можно реализовать с помощью списков, разделенных запятыми. Вот предлагаемый интерфейс, который использует только expl3
синтаксис, считающийся разработчиками стабильным:
\buildmacro{<list identifier>}
\addmacro{<list identifier>}{<item to be added>}
\fetchmacro{<list identifier>}{<index of item to be fetched>}
\listmacro{<list identifier>}
Первая настраивает все, инициализируя новый список. Вторая добавляет элемент в список. Третья извлекает элемент из списка. Четвертая последовательно перечисляет все элементы в списке.
Доказательство концепции (хотя вряд ли оригинальное):
\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}
решение3
А \@namedef
и \@nameuse
версия, подделывающая имена «макросов», что было бы невозможно без изменения кода кат.
Выделение \newcounter
внутренней части макросов может вызвать проблемы с регистрами счетчиков.
\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}
решение4
Эффект, который вы демонстрируете в своем псевдокоде, гораздо проще получить с помощью макроса списка, который позволяет выполнять цикл, не зная, сколько в нем объектов, и не используя счетчики шагов.
\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}
«Абстрактная» версия:
\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}
Еще проще с 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}
Абстрактные версии
Классический метод
\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}
Сexpl3
\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}