3

I am new to TeX and LaTeX, and I am having trouble defining and using macros that define other macros. I've read about how to do so (for example, https://en.wikibooks.org/wiki/LaTeX/Plain_TeX#Plain_TeX_macros, as well as various previous questions here on this site), but I just haven't been able to get it to work.

Consider the following intentionally verbose snippet:

\def\apcodeEWR {EWR}
\def\apnameEWR {Newark Liberty International Airport}
\def\apcityEWR {Newark, NJ, USA}

\def\apcodeORY {ORY}
\def\apnameORY {Paris Orly Airport}
\def\apcityORY {Orly, France}

\def\apdescriptionEWR {\apnameEWR, located in \apcityEWR, has the airport code \apcodeEWR.}
\def\apdescriptionORY {\apnameORY, located in \apcityORY, has the airport code \apcodeORY.}

\begin{itemize}
    \item \apdescriptionEWR
    \item \apdescriptionORY
\end{itemize}

This outputs what I would expect:

* Newark Liberty International Airport, located in Newark, NJ, USA, has the airport code EWR.
* Paris Orly Airport, located in Orly, France, has the airport code ORY.

But I would like to instead do something like:

\apdefine{EWR}{Newark Liberty International Airport}{Newark, NJ, USA}
\apdefine{ORY}{Paris Orly Airport}{Paris, France}

\begin{itemize}
    \apdescribe{EWR}
    \apdescribe{ORY}
\end{itemize}

And I'd like to get past that hurdle first, but I feel I should just mention that ultimately, I'd really like to do something like this:

\begin{itemize}
    \foreachairport\apdescribe
\end{itemize}

So, now I'm trying to make that "apdefine" macro. Based on the book that I linked to earlier, I'm trying this (having previously tried a bunch of other things):

\def\apdefine#1#2#3 {
    \expandafter\def\csnameapcode#1\endcsname {##1}
    \expandafter\def\csnameapname#1\endcsname {##2}
    \expandafter\def\csnameapcity#1\endcsname {##3}
}

That compiles without error. But then I try to use it:

\apdefine{IBA}{Ibadan Airport}{Ibadan, Oyo, Nigeria}

I get nine errors, all on that line. They are three groups of the following three:

Undefined control sequence. ...IBA}{Ibadan Airport}{Ibadan, Oyo, Nigeria}
Missing control sequence inserted. ...IBA}{Ibadan Airport}{Ibadan, Oyo, Nigeria}
Illegal parameter number in definition of \inaccessible. ...IBA}{Ibadan Airport}{Ibadan, Oyo, Nigeria}

What am I doing wrong? How do I do it correctly? Or if there's a fundamentally different approach that can be used to do what I want, what's that? Thanks.

  • Welcome to TeX.SE. It should be noted that your main mistake is that you need a space after \csname. Your other problem is that the ## in your \def would become a single #, so that your command would become \def\apacodeEWR{#1}. The existing answers handle this problem (and nicely describe how you can achieve your ultimate goal). – Teepeemm Jul 1 at 20:09
4

A call to \apdefine defines the three argument variables, as well as the description variable. Then, it appends the airport code to a comma separated list named \airportlist.

After each airport is defined via \apdefine, one should invoke the \readlist, which will create a listofitems array named \airports, which can conveniently be used in a \foreachitem loop.

\documentclass{article}
\usepackage{listofitems}
\ignoreemptyitems
\def\airportlist{}
\newcommand\apdefine[3]{%
  \expandafter\def\csname apcode#1\endcsname{#1}%
  \expandafter\def\csname apname#1\endcsname{#2}%
  \expandafter\def\csname apcity#1\endcsname{#3}%
  \expandafter\def\csname apdescription#1\endcsname{%
    #2, located in #3, has the airport code #1.}%
  \expandafter\def\expandafter\airportlist\expandafter{\airportlist#1,}
}
\apdefine{EWR}{Newark Liberty International Airport}{Newark, NJ, USA}
\apdefine{ORY}{Paris Orly Airport}{Paris, France}
\apdefine{IBA}{Ibadan Airport}{Ibadan, Oyo, Nigeria}
\readlist\airports{\airportlist}
\begin{document}
\begin{itemize}
    \foreachitem\z\in\airports[]{\item \csname apdescription\z\endcsname}
\end{itemize}
\end{document}

enter image description here

1

You can use the prop lists of expl3 to store the relevant data for each entry, and then store the key to each entry in a seq list. Defining a command, say, \apdescribetext which takes the text as argument, you can loop through the seq list and print the text.

\apdefine takes three arguments, the airport code, name, and address.
\apdescribe takes an airport code as argument and prints its info using \apdescribetext.
\apdescribeall loops through the list of airports and prints every one of them.

Here's the code:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \g_vesterman_airports_seq
\NewDocumentCommand \apdefine { mmm }
  {
    \seq_gput_right:Nn \g_vesterman_airports_seq {#1}
    \prop_new:c { g_vesterman_#1_prop }
    \prop_gput:cnn { g_vesterman_#1_prop } { name } {#2}
    \prop_gput:cnn { g_vesterman_#1_prop } { address } {#3}
  }
\NewDocumentCommand \apdescribe { m }
  {
    \exp_args:Nff \apdescribetext
      { \prop_item:cn { g_vesterman_#1_prop } { name } }
      { \prop_item:cn { g_vesterman_#1_prop } { address } }
      {#1}
  }
\NewDocumentCommand \apdescribeall { }
  { \seq_map_function:NN \g_vesterman_airports_seq \apdescribe }
\ExplSyntaxOff

\newcommand{\apdescribetext}[3]{\item #1, located in #2, has the airport code #3.}

\apdefine{EWR}{Newark Liberty International Airport}{Newark, NJ, USA}
\apdefine{ORY}{Paris Orly Airport}{Paris, France}

\begin{document}
\begin{itemize}
    \apdescribeall
\end{itemize}
\end{document}

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.