xparse and key value arguments
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?:
\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>}
):
\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).
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_item: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. ;-)