Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm trying to learn how to do macros.

I want to use xparse as it seems the most powerful tool. I also want named arguments, as numbered arguments make me, as a programmer, uncomfortable.

I had some ideas like:

\DeclareDocumentCommand{\myMacro} {o o}
{
   \DeclareDocumentCommand{\foo} {} {#1}
   \DeclareDocumentCommand{\bar} {} {#2}
   The foo argument was \foo and the bar argument was \bar

}

But this wouldn't work, because of nesting and wouldn't extend to DocumentEnviroments, I guess since different scoping for beginning and end sections.

My goal is to have some sections of a document that are highly structured. For example I might have a list of movies, which have parameters like title, date, director etc. and I might manipulate them in complex ways (perhaps building them into a paragraph), and I don't want to have to remember which order the parameters were in when defining this.

Perhaps I am thinking wrong for a LaTeX user, I guess in many programming languages you need to know the order when invoking a function (though in this case, I would like to use the name of arguments when invoking - for clarity)

share|improve this question
    
See also Latex, named parameter macros –  Martin Schröder Jun 21 '13 at 16:29
    
Thanks, I was meaning to link that here, after i created it, but I got distracted. –  Oxinabox Jun 22 '13 at 9:28

2 Answers 2

up vote 8 down vote accepted

Here is a very elementary view on LaTeX's key-value system using keyval, similar to what was presented in How to create a command with key values?:

enter image description here

\documentclass{article}
\usepackage{keyval,xparse}% http://ctan.org/pkg/{keyval,xparse}

\makeatletter
% ========= KEY DEFINITIONS =========
\define@key{mymacro}{first}{\def\mm@first{#1}}
\define@key{mymacro}{second}{\def\mm@second{#1}}
\define@key{mymacro}{third}{\def\mm@third{#1}}
\define@key{mymacro}{last}{\def\mm@last{#1}}
\DeclareDocumentCommand{\myMacro}{m}{%
  \begingroup%
  % ========= KEY DEFAULTS + new ones =========
  \setkeys{mymacro}{first={FIRST arg},second={SECOND arg},third={THIRD arg},last={LAST arg},#1}%
  First arg: \mm@first \par
  Second arg: \mm@second \par
  Third arg: \mm@third \par
  Last arg: \mm@last
  \endgroup%
}
\makeatother

\begin{document}

\myMacro{last=LaSt,first=FiRsT} \par \hrulefill

\myMacro{} \par \hrulefill

\myMacro{third={$x^2$ \textbf{stuff}},second={}}
\end{document}

It should be evident that using a key-value approach removes the requirement to remember the order of the keys. That is, unless the keys have some interaction with one another.

Grouping (via \begingroup...\endgroup) localizes the changes to the keys. However, you can make things available globally. It just depends on your application. Moreover, it would be possible to adapt the key-values to actually create key-macros that you can re-use. This may be helpful if you have a area in your document where you perform a bunch of declarations, and want to re-use them later.

The above example translates just as well to document environments.

More extensions are provided by xkeyval, ltxkeys and even l3keys from expl3 (see Programming key–value in expl3).


Here's a practical example of using key-values to store data regarding movies through a movie declaration (\newMovie{<tag>}{<key-values>}) and printing it (\showMovie{<tag>}):

enter image description here

\documentclass{article}
\usepackage{keyval,xparse,url}% http://ctan.org/pkg/{keyval,xparse,url}

\makeatletter
% ========= KEY DEFINITIONS =========
\define@key{movie}{title}{\expandafter\def\csname \movietag @title\endcsname{#1}}
\define@key{movie}{releaseyear}{\expandafter\def\csname \movietag @releaseyear\endcsname{#1}}
\define@key{movie}{genre}{\expandafter\def\csname \movietag @genre\endcsname{#1}}
\define@key{movie}{url}{\expandafter\def\csname \movietag @url\endcsname{#1}}
\DeclareDocumentCommand{\newMovie}{m m}{%
  % ========= KEY DEFAULTS + new ones =========
  \def\movietag{#1}% Store movie tag (used when setting/storing keys)
  \setkeys{movie}{title={},releaseyear={},genre={},url={},#2}% Create keys
}
\DeclareDocumentCommand{\showMovie}{m}{%
  \textbf{\csname #1@title\endcsname} (\csname #1@releaseyear\endcsname),
  \textit{\csname #1@genre\endcsname},
  \expandafter\expandafter\expandafter\url\expandafter\expandafter\expandafter{\csname #1@url\endcsname}.
}
\makeatother

\begin{document}

% Movie declarations
\newMovie{anchorman2}{
  url={http://www.imdb.com/title/tt1229340/},
  releaseyear=2013,
  title={Anchorman: The Legend Continues},
  genre=Comedy}
\newMovie{lego}{
  title={The Lego Movie},
  releaseyear=2014,
  url={http://www.imdb.com/title/tt1490017/},
  genre={Animation, Action, Comedy}}
\newMovie{magic}{
  title={Now You See Me},
  url={http://www.imdb.com/title/tt1670345/},
  releaseyear=2013,
  genre={Crime, Thriller}}

% Show movie details
\showMovie{magic}

\showMovie{anchorman2}

\showMovie{lego}

\end{document}

There are other (perhaps better) ways of dealing with this, but again, this is just to showcase what you can do with key-value approaches to macros (and databases).

share|improve this answer

An implementation entirely in expl3, using key-value pairs and property lists.

Each \newMovie command defines a property list and stores the values. Then \showMovie displays them in the preferred order.

\documentclass{article}
\usepackage{xparse,url}

\ExplSyntaxOn
% keys
\keys_define:nn { oxinabox/movies }
 {
  title .tl_set:N = \l_oxinabox_title_tl,
  releaseyear .tl_set:N = \l_oxinabox_releaseyear_tl,
  genre .tl_set:N = \l_oxinabox_genre_tl,
  url .tl_set:N = \l_oxinabox_url_tl,
 }

% user level commands
\NewDocumentCommand{\newMovie}{m m}
 {
  \oxinabox_newmovie:nn { #1 } { #2 }
 }

\NewDocumentCommand{\showMovie}{m}
 {
  \oxinabox_showmovie:n { #1 }
 }

% internal functions
\cs_new_protected:Npn \oxinabox_newmovie:nn #1 #2
 {
  \group_begin: % keep the assignment to the keys local
  \prop_new:c { g_oxinabox_movie_#1_prop }
  \keys_set:nn { oxinabox/movies } { #2 }
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { title } \l_oxinabox_title_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { releaseyear } \l_oxinabox_releaseyear_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { genre } \l_oxinabox_genre_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { url } \l_oxinabox_url_tl
  \group_end:
 }

\cs_new:Npn \oxinabox_getvalue:nn #1 #2
 {
  \prop_get:cn { g_oxinabox_movie_#1_prop } { #2 }
 }
\cs_new_protected:Npn \oxinabox_showmovie:n #1
 {
  \par\noindent
  \textbf{ \oxinabox_getvalue:nn { #1 } { title } }, ~ %
  \textbf{ \oxinabox_getvalue:nn { #1 } { releaseyear } }, ~ %
  \textbf{ \oxinabox_getvalue:nn { #1 } { genre } },
  \\
  \use:x { \exp_not:N \url { \oxinabox_getvalue:nn { #1 } { url } } }
}
\ExplSyntaxOff
\begin{document}

% Movie declarations
\newMovie{anchorman2}{
  url={http://www.imdb.com/title/tt1229340/},
  releaseyear=2013,
  title={Anchorman: The Legend Continues},
  genre=Comedy}
\newMovie{lego}{
  title={The Lego Movie},
  releaseyear=2014,
  url={http://www.imdb.com/title/tt1490017/},
  genre={Animation, Action, Comedy}}
\newMovie{magic}{
  title={Now You See Me},
  url={http://www.imdb.com/title/tt1670345/},
  releaseyear=2013,
  genre={Crime, Thriller}}

% Show movie details
\showMovie{magic}

\showMovie{anchorman2}

\showMovie{lego}

\end{document}

Thanks to Werner for the data. ;-)

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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