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
add comment

3 Answers

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
add comment

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 } } , },
 }

\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
add comment

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
add comment

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.