4

In plain TeX, how do I create a stack of items? I need to be able to push items to the end of the list, pop items and output the full list of items.

For example

\createlist{\mylist}
\push{\mylist}{A}
\push{\mylist}{-B}
\push{\mylist}{-C}
\print{\mylist}   % prints A-B-C
\pop{\mylist}     % Removes `-C'
\print{\mylist}   % prints A-B

I found listings.sty which seems to have pushing (with \lst@lAddToa) but not popping (although I am not sure as the documentation of this functionality is thin).

2 Answers 2

7

A would expect a stack to build up from the front not the end, so here I reverse the stack for \print

enter image description here

This assumes the tokens in the items are safe in an \edef

\catcode`\!=4
\def\createlist#1{\def#1{}}
\def\push#1#2{\edef#1{#2!#1}}
\def\pop#1{\edef#1{\expandafter\xpop#1}}
\def\xpop#1!{}
\def\print#1{\expandafter\xprint#1!\xpop!\relax}
\def\xprint#1!#2\relax{\ifx\xpop#1\else\xprint#2\relax#1\fi}
\catcode`\!=12


\createlist{\mylist}

\push{\mylist}{A}

\push{\mylist}{-B}

\push{\mylist}{-C}

\print{\mylist}   % prints A-B-C

\pop{\mylist}     % Removes `-C'

\print{\mylist}   % prints A-B


\bye
5
  • I absolutely love this solution. I am sill learning tex and the idea of using ! and \def parsing to find the next item is genius.
    – Tohiko
    Feb 29, 2020 at 14:25
  • I am trying to modify the script to include a \lastpop (mainly to test my understanding of you script). I thought I could just do \def\xpop#1!{\def\lastpop{#1}}, so that when an item is discarded it is saved in \lastpop (I think I probably will have to define a separate command that is used in pop than print to avoid overriding lastpop when printing). However, that doesn't seem to work since the \def command inside \xpop seems to never be called.
    – Tohiko
    Feb 29, 2020 at 19:30
  • @Tohiko you would have to re-arrange it slightly to do the \edef of te rest of the stack later, I do it inside the edef here so you can not do a non-expandable assignment of the popped value Feb 29, 2020 at 19:32
  • Ah I see. I think I figured it out (although there are probably more elegant solutions). \def\pop#1{\expandafter\deflastpop#1\relax \edef#1{\expandafter\xpop#1}} \def\deflastpop#1!#2\relax{\def\lastpop{#1} \xignore#2!\xignore!\relax} \def\xignore#1!#2\relax{\ifx\xignore#1\else\xignore#2\relax\fi}
    – Tohiko
    Feb 29, 2020 at 19:50
  • 1
    @Tohiko Not tested, but something like \def\pop#1{\expandafter\xpop#1\xpop#1} \def\xpop#1!#2\xpop#3{\def\lastpop{#1}\def#3{#2}} should do, with the restriction that you cannot have \xpop in an item of the stack. Also note that with your code you can't have \relax Mar 1, 2020 at 1:47
5

Just for completeness, if you're using a TeX engine with ε-TeX (that is, not Knuth TeX), then you can use seq variables from expl3, which have most of the stack functionality you want:

\input expl3-generic

\ExplSyntaxOn
\tl_new:N \lastpop
\cs_new_eq:NN \createlist \seq_new:N
\cs_new_eq:NN \push \seq_push:Nn
\cs_new_protected:Npn \pop #1
  { \seq_pop:NN #1 \lastpop }
\cs_new_protected:Npn \print #1
  {
    \tl_clear:N \lastpop
    \seq_map_inline:Nn #1
      { \tl_put_left:Nn \lastpop {##1} }
    \tl_use:N \lastpop
  }
\ExplSyntaxOff

\createlist{\mylist}
\push{\mylist}{A}
\push{\mylist}{-B}
\push{\mylist}{-C}
\print{\mylist}   % prints A-B-C
\pop{\mylist}     % Removes `-C' (`-C' is available in \lastpop)
\print{\mylist}   % prints A-B

\bye

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.