17

I want to know how I can store variables so I can iterate over them.

Say we have this input in the document:

\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}

Later in the document we write something like \outputinreverseorder and get “Input 3 is ‘third input’. Input 2 is ‘second input’. Input 1 is ‘first input’.” Writing something like \outputinorder gives the opposite order.

1
  • 2
    Depending on how you want it to behave exactly, you might want to have a look at Stack datastructure using LaTeX , they deal with many issues, like datastructures themselves, verbatim text in the arguments etc. Commented May 17, 2012 at 10:15

5 Answers 5

15

Here's an implementation with expl3:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

% first of all we define the user level commands
\NewDocumentCommand{\inputcommandname}{ m }{ \malmedal_input_add:n { #1 } }
\NewDocumentCommand{\outputinorder}{ }{ \malmedal_output_direct: }
\NewDocumentCommand{\outputinreverseorder}{ }{ \malmedal_output_reverse: }

% allocate some variables:
% 1. a sequence for global storage of the inputs;
% 2. a temporary sequence when we need to reverse;
% 3. a counter (integer variable)
\seq_new:N \g_malmedal_input_seq
\seq_new:N \l_malmedal_temp_seq
\int_new:N \l_malmedal_count_int

% store globally an input in the sequence    
\cs_new:Npn \malmedal_input_add:n #1
 {
  \seq_gput_right:Nn \g_malmedal_input_seq { #1 }
 }

% how to output in direct order; we simply do a mapping function calling
% \malmedal_print:n after incrementing the counter
\cs_new_protected:Npn \malmedal_output_direct:
 {
  \int_zero:N \l_malmedal_count_int
  \seq_map_inline:Nn \g_malmedal_input_seq
   {
    \int_incr:N \l_malmedal_count_int
    \malmedal_print:n { ##1 }
   }
 }

% how to output in reverse order; first we store a copy of the main
% sequence in the temporary one, that we reverse; then we set the
% integer variable to the length of the sequence; finally we map the
% temporary sequence, decrementing the counter after having printed
% its contents
\cs_new_protected:Npn \malmedal_output_reverse:
 {
  \seq_set_eq:NN \l_malmedal_temp_seq \g_malmedal_input_seq
  \seq_reverse:N \l_malmedal_temp_seq
  \int_set:Nn \l_malmedal_count_int { \seq_length:N \l_malmedal_temp_seq }
  \seq_map_inline:Nn \l_malmedal_temp_seq
   {
    \malmedal_print:n { ##1 }
    \int_decr:N \l_malmedal_count_int
   }
 }

% the printing macro; change here for adapting to your wishes
\cs_new:Npn \malmedal_print:n #1
 {
  Input ~ \int_to_arabic:n { \l_malmedal_count_int } ~ is ~ `#1' \par
 }
\ExplSyntaxOff

\begin{document}

\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}

Here is direct order:

\outputinorder

\bigskip

Here is reverse order:

\outputinreverseorder

\end{document}

Each \inputcommandname adds globally its argument to a sequence which we can use for printing in direct order or in reverse order by reversing it.

The output is

Here is direct order:
Input 1 is ‘first input’
Input 2 is ‘second input’
Input 3 is ‘third input’

Here is reverse order:
Input 3 is ‘third input’
Input 2 is ‘second input’
Input 1 is ‘first input’


Due to the changes made to expl3 in Summer 2012, in the code above \seq_length should be replaced by \seq_count

\cs_new_protected:Npn \malmedal_output_reverse:
 {
  \seq_set_eq:NN \l_malmedal_temp_seq \g_malmedal_input_seq
  \seq_reverse:N \l_malmedal_temp_seq
  \int_set:Nn \l_malmedal_count_int { \seq_count:N \l_malmedal_temp_seq }
  \seq_map_inline:Nn \l_malmedal_temp_seq
   {
    \malmedal_print:n { ##1 }
    \int_decr:N \l_malmedal_count_int
   }
 }
16

And here comes a LuaTeX solution. It's ConTeXt flavoured, but it can be easily adapted to LaTeX as well.

\startluacode

  userdata = { }
  userdata.list = { }
  local list = userdata.list

  userdata.addtolist = function (item)
    userdata.list[#list+1] = item
  end

  userdata.outputinorder = function ()
    for i=1, #list do
      context(list[i])
      context.crlf()
    end
  end

  userdata.outputinreverseorder = function ()
    for i=#list, 1, -1 do
      context(list[i])
      context.crlf()
    end
  end

\stopluacode

\define[1]\inputcommandname{\ctxlua{userdata.addtolist("#1")}}
\define   \outputinorder{\ctxlua{userdata.outputinorder()}}
\define   \outputinreverseorder{\ctxlua{userdata.outputinreverseorder()}}

\starttext
  \inputcommandname{first input}
  \inputcommandname{second input}
  \inputcommandname{third input}

  \outputinorder
  \blank
  \outputinreverseorder
\stoptext

It simply stores the values in a table and outputs the table in the requested order.

screenshot

15

I suppose this isn't so different from Martin's but here is a plain TeX solution

enter image description here

\def\stack{}
\def\stackcount{0}

\def\inputcommandname#1{{%
\let\xdo\relax
\count0=\stackcount\relax
\advance\count0 by 1
\xdef\stackcount{\the\count0}%
\toks0{\xdo{#1}}%
\toks2\expandafter{\stack}%
\xdef\stack{\the\toks2 \the\toks0 }}}


\def\outputinorder{{\count0=0
\def\xdo##1{\advance\count0 by 1
Input \the\count0 \space is: `##1'. }%
\stack}}

\def\outputinreverseorder{{%
\def\xdo##1##2\midstack##3\empty{%
  ##2\midstack\xdo{##1}##3\empty}%
\stack\midstack\empty}}

\def\midstack{%
\count0=\stackcount
\def\xdo##1{%
Input \the\count0 \space is: `##1'. 
\advance\count0 by -1 }}



\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}


\outputinorder 


\outputinreverseorder 

\bye
1
  • +1 for the reversing wizardry in \outputinreverseorder and for using a stack. I would have saved also the \count0 values in the stack, to avoid \advanceing it on the pop.
    – Ahmed Musa
    Commented May 17, 2012 at 15:34
14

You can use \@namedef and \@nameuse to define the input values to internal macros numbered by a running counter. This counter then can be used to iterate over the elements.

Alternatives might be the stack package which AFAIK provides functionality like this as well. Also the etoolbox package provides macros to define lists and iterate over them. However, I didn't saw anything about reversing the order.


Here some principle code:

\documentclass{article}

\newcounter{inputcommandname}
\makeatletter
\newcommand\inputcommandname[1]{%
    \stepcounter{inputcommandname}%
    \@namedef{inputcommandname@\theinputcommandname}{#1}%
}
\newcommand\getinputcommandname[1]{%
    \@nameuse{inputcommandname@\number#1}%
}

\newcommand\outputinreverseorder{%
    \@tempcnta=\value{inputcommandname}
    \loop
      \@nameuse{inputcommandname@\number\@tempcnta}%
      \advance\@tempcnta by -1
      \ifnum\@tempcnta>0
        \separator
    \repeat
}

\newcommand\outputinorder{%
    \@tempcnta=1
    \loop
      \@nameuse{inputcommandname@\number\@tempcnta}%
      \ifnum\@tempcnta<\value{inputcommandname}%
        \advance\@tempcnta by 1
        \separator
    \repeat
}

\newcommand\separator{, }
\makeatother

\begin{document}

\inputcommandname{first input}
\inputcommandname{second input}
\inputcommandname{third input}

% Second input:
\getinputcommandname{2}

% In reverse order
\outputinreverseorder

% In order
\outputinorder

\end{document}

This gives:

second input
third input, second input, first input
first input, second input, third input
11

Here is an extension/generalization of David Carlisle's solution. (1) Several stacks could be created for different purposes using the command \inputcommandnames; (2) the command \inputcommandnames or \inputcommandname can be called with more than one command name for each stack; the commands \outputinnormalorder and \outputinreverseorder can be called with more than one stack name; (4) the stack counter is called only on push, and not on pop. See the examples.

\documentclass{article}
\usepackage{catoptions}
\makeatletter
\robust@def*\inputcommandnames{\cpt@testopt\@inputcommandnames{general}}
\newletcs\inputcommandname\inputcommandnames
% #1=stack name, #2=input name(s)
\robust@def*\@inputcommandnames[#1]#2{%
  \ifblankFT{#1}{%
    \@latex@info{No stack name given: using 'general' instead}%
  }{%
    % It is possible to iterate over stack names, but leave this for now:
    % the use case isn't yet apparent.
    \ifinsetTF,{#1}{%
      \@latexerr{List '#1' not allowed}\@ehd
    }{}%
  }%
  \ifcsndefTF{name@stack@#1}{}{\csn@def{name@stack@#1}{}}%
  \ifcsndefTF{name@stackcount@#1}{}{\csn@def{name@stackcount@#1}{0}}%
  \cptdocommalist{#2}{%
    \csn@edef{name@stackcount@#1}%
      {\the\numexpr\usename{name@stackcount@#1}+1}%
    \csn@edef{name@stack@#1}{%
      \expandcsnonce{name@stack@#1}\noexpand
      \do{\usename{name@stackcount@#1}}{\unexpanded{##1}}%
    }%
  }%
}
\robust@def*\outputinnormalorder{\@outputcommandnames{normal}}
\newletcs\outputcommandnames\outputinnormalorder
\robust@def*\outputinreverseorder{\@outputcommandnames{reverse}}
% #1=listing/printing order, #2=stack name(s)
\robust@def*\@outputcommandnames#1#2{%
  \cptdocommalist{#2}{%
    \begingroup
    \let\reserved@a\relax
    \ifcsndefTF{name@stack@##1}{%
      % If the stack is defined but it is currently empty, ignore it
      % (but issue a warning). A 'verbose' option for triggering the
      % warning will be useful here, but it hasn't been provided:
      \ifcsnnullFT{name@stack@##1}{}{%
        \@latex@info{Stack '##1' is empty}%
        \def\reserved@a####1\endgroup{\endgroup}%
      }%
    }{%
      \@latexerr{Stack '##1' is not defined}\@ehd
    }%
    \reserved@a
    \xifstrcmpTF{#1}{normal}{%
      \let\do\curroutputformat
      \csname name@stack@##1\endcsname
    }{%
      \def\name@sentinel####1\name@stop{%
        \let\do\curroutputformat####1%
      }%
      \def\do####1####2####3\name@sentinel####4\name@stop{%
        ####3\name@sentinel\do{####1}{####2}####4\name@stop
      }%
      \usename{name@stack@##1}\name@sentinel\name@stop
    }%
    \endgroup
  }%
}
% \commandnameoutputformat<input no.><input item>
\robust@def*\commandnameoutputformat{%
  % Skip any spurious spaces before opening brace:
  \begingroup
  \toks0{##1##2}%
  \def\reserved@a{%
    \expandafter\endgroup\expandafter\def\expandafter
    \curroutputformat\the\toks\expandafter0\expandafter{\the\toks1}%
  }%
  \afterassignment\reserved@a\toks1=%
}
% If the stack isn't initialized after use, it will start building
% from the last count whenever it is called:
\robust@def*\initializecommandstacks#1{%
  \cptdocommalist{#1}{%
    \csn@def{name@stackcount@##1}{0}%
    \csn@def{name@stack@##1}{}%
  }%
}
\newletcs\initializecommandstack\initializecommandstacks

% Set empty stack 'xx' for warning later:
\csn@def{name@stack@xx}{}
\makeatother

% Examples:
\begin{document}
\inputcommandname{first input (general),second input (general),third input (general)}
\inputcommandname[stack-1]{First Input (stack-1),Second Input (stack-1),Third Input (stack-1)}
\commandnameoutputformat{Input number #1 is: #2.\par}
Normal order Output:\par
\outputinnormalorder{general,stack-1}
\par\medskip
Reverse order Output:\par
\outputinreverseorder{general,stack-1}
\par\medskip
Normal order Output:\par
\outputinnormalorder{stack-1}
\par\medskip
Reverse order Output:\par
\outputinreverseorder{stack-1}
% Issue a warning for empty stack 'xx' and continue:
\outputcommandnames{xx}
\outputinreverseorder{general}
\end{document} 

enter image description here

2
  • 1
    Maybe you could elaborate a bit what exactly is your extension/generalization?
    – Daniel
    Commented May 17, 2012 at 19:14
  • @Daniel: I have added some note.
    – Ahmed Musa
    Commented May 17, 2012 at 20:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .