6

I would like to define a TeX 'macro-building' command (let us call it \buildmacro) that takes as argument a string of 'normal' characters (e.g., Title) and which satisfies the followings:

  • the command \buildmacro{Title} defines the counter with the name TitleXcounter and initializes it to 0.
  • when issuing at some point in the file the TeX command \Title{<arg>}, this will have the effect of increasing the counter TitleXcounter and then putting the value <arg> into a new TeX macro with the name \Title-<x>, where <x> is the current value value of TitleXcounter;

    here, <arg> is a piece of TeX code (that can contain text, maths, paragraphs of any other TeX commands).

Consider the following minimal example:

\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}

I 'played' with \csname and \endcsname, \expandafter, \def, \gdef and the \long versions, but with no 'luck'.

Edited (in order to answer some of the comments):

The purpose of this is to have a simple mechanism to create a set of indexed commands with the same root-name (similar to a list of commands). One can always use the \csname <...> \endcsname construct to issue the commnds.

  • You need to rethink your requirements so that they are consistent with legal macro names. No macro name can include - or a number. You need to stick to letters, basically. (@ if @ is currently a letter or whatever is also OK. But that means a catcode change to make it a letter. Same for expl3 syntax with _ and : being allowed.) Moreover, \title is a core LaTeX command. – cfr Jun 12 '16 at 1:34
  • 1
    This really sounds like an XY problem to me. What are you actually trying to do? Note that there's a good reason to stick to \newcommand and friends in LaTeX, namely they check you aren't overwriting stuff which is already defined. If you want to overwrite stuff, use \renewcommand or whatever. – cfr Jun 12 '16 at 1:37
  • A basic 'macro maker' might look like this: \newcommand\cmdmaker[2]{\expandafter\newcommand\csname #1\endcsname{\textbf{#2}}}, where the usage would be like \cmdmaker{firstone}{Look at this!} with you being able to use \firstone in the body of your .tex file. But I really don't get the point/goal of the counter stuff.... – jon Jun 12 '16 at 2:15
  • I don't see why there is a problem with the numbers. I just need to create a set of indexed commands with the same root-name. One can always use the \csname <...> \endcsname construct. Of course, \title was an unfortunate choice, but we can uppercase it to \TITLE, so there is no conflict. – digital-Ink Jun 12 '16 at 7:43
  • @digital-Ink: Making 1 etc. catcode 11 is easy, but I think,you get into troubles when defining some macro number argument, i.e. [1] etc. – user31729 Jun 12 '16 at 9:27
6

From the description of the flow required this seems quite simple

\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}%
  }%
}

As a plain version was also requested

\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}%
  }%
}
  • In the plain version, the commands are not global. If I issue \Title{First Title} inside a group, then it has no effect used outside of the group. Can these be made global? – digital-Ink Jun 12 '16 at 12:06
  • I figure it out, eventually, I used \gdef instead of the last \def and worked. – digital-Ink Jun 12 '16 at 12:09
  • @digital-Ink Your example has not groups so I don't follow: presumably there is more to your real use case – Joseph Wright Jun 12 '16 at 12:45
6

As I mentioned in a comment, neither a hyphen nor a numeral can be used when constructing macro names unless you will always write the equivalent of \csname ... \endcsname or ensure the catcodes are changed when using them.

Hence, it is necessary to modify the user interface from that suggested.

The basic idea seems to be to create a series of indexed lists of argument which can be retrieved by giving the name of the list and the index of the item within that list.

This could be implemented using comma-separated lists. Here is a suggested interface which uses only expl3 syntax considered stable by the developers:

\buildmacro{<list identifier>}
\addmacro{<list identifier>}{<item to be added>}
\fetchmacro{<list identifier>}{<index of item to be fetched>}
\listmacro{<list identifier>}

The first sets things up by initialising a new list. The second adds an item to a list. The third fetches an item from a list. The fourth lists all items in a list sequentially.

Proof of concept (though hardly an original one):

\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}

proof of concept

  • This looks interesting, but there may be compatibility issues, since LaTeX3 is still under development. A LaTeX2e or/and Plain TeX approach would be preferable. – digital-Ink Jun 12 '16 at 8:34
  • 1
    the first sentence is misleading (or wrong, depending how you read it) catcodes are not so relevant if you are constructing a csname \csname a124-6^ \endcsname is perfectly legal. Only thing to watch out for is catcode13, active where it will be the expansion of the character not the character itself that is used. – David Carlisle Jun 12 '16 at 9:14
  • 1
    @digital-Ink: LaTeX3 is stable in many cases and as far I can see, cfr did not use any experimental features. – user31729 Jun 12 '16 at 9:23
  • @DavidCarlisle Thanks. I was thinking of when you came to use it, but, for some reason, I wrote something different. Is it better now? I'm very hazy about catcodes. – cfr Jun 12 '16 at 12:58
  • 2
    @digital-Ink As Christian said, this isn't using anything experimental. I'm only using stuff considered stable by the developers. My understanding is that the non-experimental part of the user interface is very unlikely to change, even though the implementation details (and so e.g. functions marked as internal) might and stuff marked experimental very probably will. You are probably already relying on expl3 stuff anyway unless you examine the source of any new package you use to ensure it is expl3-free. – cfr Jun 12 '16 at 13:03
4

A \@namedef and \@nameuse version, faking 'macro' names that would not be possible without catcode change.

The allocation of \newcounter inside of macros may cause troubles with counter registers.

\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}

enter image description here

3

The effect you're showing in your pseudocode is much more easily obtained with a list macro, that allows to do a loop without having to know how many objects are in it and without stepping counters.

\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}

enter image description here

An “abstract” 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}

Even easier with 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}

enter image description here


Abstract versions

Classic method

\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}

With 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}
  • I dont't want to define the \Title command, this was just an example. In place of Title can be (almost) ant string of text. – digital-Ink Jun 12 '16 at 10:23
  • 1
    @digital-Ink Added abstract versions – egreg Jun 12 '16 at 10:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.