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 define a list environment with multiple optional arguments and with a default values for those arguments. For example, creating a new environment with a name newenv, which accepts two arguments, and if they aren't present uses default values:

\documentclass{article}
\usepackage{array}
\usepackage{multicol}
\usepackage{enumerate}

\begin{document}

\newenvironment{newenv}[2][2]
{\begin{multicols}[#1]\begin{enumerate}[#2]}
{\end{enumerate}\end{multicols}}

\begin{newenv}{2}{2}
  \item a
  \item b
  \item c
  \item d
\end{newenv}

\end{document}

What am I missing?

share|improve this question
5  
The default \newenvironment (like \newcommand and friends) accepts only one optional argument. For multiple optional arguments, consider the xparse interface: \NewDocumentEnvironment{newenv}{o o}{<beg env>}{<end env>}. However, with multiple optional arguments, the only way to specify the second, would be to also specify the first, even if it could be empty. How else would the parser know that the only optional argument is the second? In such instances, a key-value approach is preferred. –  Werner Feb 15 '13 at 17:57
2  
have a look at More than one optional argument for newcommand which is for a newcommand, but the syntax is pretty much identical for newenvironment –  cmhughes Feb 15 '13 at 18:01

3 Answers 3

I would use the enumitem package for this- it eases the syntax and does the heavy lifting for us

The first part is to setup a new list environment

\newlist{newenv}{enumerate}{5}
\setlist[newenv]{label=\arabic*.}

which has a default label of 1., 2., etc

We then setup a new key, columns

\SetEnumitemKey{columns}{before=\begin{multicols}{#1},
                         after=\end{multicols}}

which allows us to use all variety of combinations, as demonstrated in the MWE below

screenshot

\documentclass{article}
\usepackage{enumitem}
\usepackage{multicol}

\newlist{newenv}{enumerate}{5}
\setlist[newenv]{label=\arabic*.}

\SetEnumitemKey{columns}{before=\begin{multicols}{#1},
                                               after=\end{multicols}}

\begin{document}
\begin{newenv}[label=\roman*),columns=4]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[columns=3]
\item a
\item b
\item c
\end{newenv}

\hrule

\begin{newenv}[label=\roman*)]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[label*=(A\arabic*),columns=3]
\item a
\item b
\item c
\item d
\item e
\item f
\end{newenv}

\end{document}
share|improve this answer
2  
+1 For me this is the best solution –  Marco Daniel Feb 15 '13 at 19:51

I agree with Werner that multiple optional arguments should be replaced by a key-value syntax. Here's a way:

\documentclass{article}
\usepackage{enumerate,multicol,xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{newenv}{ O{} }
 {
  \keys_set:nn { aksr/newenv }
   {
    #1           % use the options
   }
  \int_compare:nT { \l__aksr_columns_tl > 1 } % more than one column
   { \begin{multicols}{ \l__aksr_columns_tl } }
  % We must pass the optional argument expanded
  \use:x { \exp_not:N \begin{enumerate}[ \l__aksr_label_tl ] }
 }
 {
  \end{enumerate}
  \int_compare:nT { \l__aksr_columns_tl > 1 }
   { \end{multicols} }
 }

\tl_new:N \l__aksr_columns_tl
\tl_new:N \l__aksr_label_tl

\keys_define:nn { aksr/newenv }
 {
  columns .tl_set:N  = \l__aksr_columns_tl,
  columns .initial:n = 2,
  label   .tl_set:N  = \l__aksr_label_tl,
  label   .initial   = 1.,
 }
\ExplSyntaxOff

\begin{document}
\begin{newenv}
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[columns=1]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[label=i)]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[label=(A),columns=3]
\item a
\item b
\item c
\item d
\item e
\item f
\end{newenv}

\end{document}

It's quite straightforward, once we have some hand with it. The only subtlety is that we must pass the argument to \begin{enumerate} expanded and not the token list variable, so \use:x comes to rescue.

The two keys just store the value in a token list variable for later usage. The defaults are simply established by giving the values before evaluating the optional argument to newenv.

You may consider adopting enumitem rather than enumerate.

enter image description here


Here's a different version that works with enumitem and shows the use of "unknown" keys:

\documentclass{article}
\usepackage{enumitem,multicol,xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{newenv}{ O{} }
 {
  % clear the options passed to enumitem
  \tl_clear:N \l__aksr_enumitem_tl
  \keys_set:nn { aksr/newenv }
   {
    #1 % use the options
   }
  \int_compare:nT { \l__aksr_columns_tl > 1 } % more than one column
   { \begin{multicols}{ \l__aksr_columns_tl } }
  % We must pass the optional argument expanded; we append
  % the label key and value to the other possibly set options
  \tl_put_right:Nn \l__aksr_enumitem_tl { label = }
  \tl_put_right:NV \l__aksr_enumitem_tl \l__aksr_label_tl
  \aksr_beginenum:V \l__aksr_enumitem_tl 
 }
 {
  \end{enumerate}
  \int_compare:nT { \l__aksr_columns_tl > 1 }
   { \end{multicols} }
 }
\cs_new_protected:Npn \aksr_beginenum:n #1
 {
  \begin{enumerate}[#1]
 }
\cs_generate_variant:Nn \aksr_beginenum:n {V}

\tl_new:N \l__aksr_columns_tl
\tl_new:N \l__aksr_label_tl
\tl_new:N \l__aksr_enumitem_tl

\keys_define:nn { aksr/newenv }
 {
  columns .tl_set:N  = \l__aksr_columns_tl,
  columns .initial:n = 2,
  label   .tl_set:N  = \l__aksr_label_tl,
  label   .initial:n = \arabic*.,
  %% unknown keys will be passed to enumitem
  unknown .code:n    = \tl_put_right:Nx \l__aksr_enumitem_tl 
                        { 
                         \l_keys_key_tl % the key name
                         \tl_if_empty:nF { #1 } % the value, if not empty
                          { = \exp_not:n { #1 } } , },
 }
\ExplSyntaxOff

\begin{document}
\begin{newenv}
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[columns=1,noitemsep]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[label=\roman*)]
\item a
\item b
\item c
\item d
\end{newenv}

\hrule

\begin{newenv}[label=(\alph*),columns=3]
\item a
\item b
\item c
\item d
\item e
\item f
\end{newenv}

\end{document}
share|improve this answer
    
+1: Very nice, but in the second MWE there is an \ExplSyntaxOff missing before \begin{document}. –  Henri Menke Jul 27 at 14:09
    
@HenriMenke Thanks for spotting! –  egreg Jul 27 at 14:23
    
@egreg If I wanted to learn to understand this syntax, what should I start by reading? I have tried to puzzle out some of the LaTeX 3 documentation before in order to learn this but everything seems to assume I already know basically how it works and my efforts have proved fruitless. It just seems too alien and quite unlike anything else I deal with. (Maybe it is more familiar to people with programming knowledge but I can basically manage a simplish shell script and nothing more.) [I have a horrible feeling I've either asked this question before or seen somebody else do so but no idea where.] –  cfr Jul 27 at 14:54
1  
@cfr I have no background in programming, other than (La)TeX. Look at texdoc expl3 and texdoc interface3; then work out some examples. –  egreg Jul 27 at 14:58

At risk of being reprimanded for the suggestion, you could use different delimiters for the different optionals. The line

\NewDocumentEnvironment{newenv}{ O{2} D<>{(a)} }

Specifies that there are two optional arguments, the first is of type O which is a standard square bracketed optional with default whatever is in the following brace group (here 2). The second optional argument is of type D ("D"elimited) with left delimiter <, right delimiter > and has default whatever is given in the following brace group, here (a).

\documentclass{article}
\usepackage{array}
\usepackage{multicol}
\usepackage{enumerate}
\usepackage{xparse}

\NewDocumentEnvironment{newenv}{ O{2} D<>{(a)} }
{\begin{multicols}{#1}\begin{enumerate}[#2]}
{\end{enumerate}\end{multicols}}

\begin{document}

\begin{newenv}[2]<i.>
  \item a
  \item b
  \item c
  \item d
\end{newenv}

\end{document}
share|improve this answer
1  
+1; but the syntax is complicated and the order must be respected. –  egreg Feb 15 '13 at 18:30
    
@egreg Both true! key-val is indeed the superior approach, I don't know it well enough to have provided a decent answer so left that for the experts and took the low hanging fruit :) –  Scott H. Feb 15 '13 at 18:49

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.