4

This is an extension of my previous question. The accepted answer provided a way for me to delay expansion of a marked-up easylist list until the string was processed inside the environment. I'm trying to adapt the code into an environment that allows convenient formatting of multiple-choice questions. MWE follows (compile with LuaLaTeX):

\documentclass[11pt]{memoir}

\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage{xstring}
\usepackage{easylist}
\usepackage{ifthen}

\newenvironment{question}{
    \newcommand{\choicestext}{}
    \newcommand{\choice}[1]{
        \edef\oldchoicestext{\choicestext}
        \edef\newchoicetext{##1}
        % just append
        \renewcommand{\choicestext}{\oldchoicestext CHOICE\newchoicetext}
    }
}{
    \edef\finchoicestext{\choicestext}
    %wow http://goo.gl/jHcwNY thank you egreg
    \begingroup\catcode`§=\active
    \noexpandarg
    \expandafter\StrSubstitute\expandafter{\finchoicestext}{CHOICE}{ § }[\tempctext]

    \begin{easylist}
        \tempctext
    \end{easylist}
    \endgroup
}

\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}

The choice command is responsible for compiling user-given choices into the \choicestext variable delimited by CHOICE, which I then want to convert into easylist format. I borrowed code from the previous answer to format this by substituting the active section character for CHOICE, but this doesn't seem to work inside my environment. How can I preserve the mark-up-then-format behavior within the question environment?

I'm sorry if my recent questions sound similar. I'm still wrestling with TeX stuff like \expandafter and the like.

  • 1
    are you sure you want to load fontenc and fontspec? loading fontenc with xetex/luatex isn't always wrong, but it usually is. – David Carlisle Mar 1 '15 at 0:38
  • since you have a sequence of \choice commands why not simply make them \item in a normal list rather than concatenate them all with § and then use easylist to split that up and make it into a list with \item ?? – David Carlisle Mar 1 '15 at 0:50
  • @DavidCarlisle Because they have to be displayed afterward. Their order (e.g. relative to the question prompt and figures, which I've omitted in this MWE) is determined precisely to be at the end of each question. – Simon Kuang Mar 1 '15 at 0:52
  • Whatever re-ordering you need to do, there must be easier ways than concatenating everything into a single token list and then splitting it up again at the same points. (But I can't guess what to suggest as I don't understand that point) – David Carlisle Mar 1 '15 at 0:59
5

as in egreg's answer you need to make the character active before making the definition.

This runs without error, I am not sure if it is the output you want?

\documentclass[11pt]{memoir}

\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage{xstring}
\usepackage{easylist}
\usepackage{ifthen}
\catcode`§=\active

\newenvironment{question}{
    \newcommand{\choicestext}{}
    \newcommand{\choice}[1]{
        \edef\oldchoicestext{\choicestext}
        \edef\newchoicetext{##1}
        % just append
        \renewcommand{\choicestext}{\oldchoicestext CHOICE\newchoicetext}
    }
}{
    \edef\finchoicestext{\choicestext}
    %wow http://goo.gl/jHcwNY thank you egreg
    \noexpandarg
    \expandafter\StrSubstitute\expandafter{\finchoicestext}{CHOICE}{ § }[\tempctext]

    \begin{easylist}
        \tempctext
    \end{easylist}
}
\catcode`\§=12

\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}

the macro seems to be doing the same as

\documentclass[11pt]{memoir}

\usepackage{fontspec}



\newenvironment{question}{%
    \begin{enumerate}
        \let\choice\item}{%
    \end{enumerate}
}


\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}

Or if you want to concatenate a string for easylist:

\documentclass[11pt]{memoir}

\usepackage{fontspec}
\usepackage{easylist}
\catcode`§=\active
\makeatletter
\newenvironment{question}{%
    \newcommand{\choicestext}{}%
    \newcommand{\choice}[1]{%
        \g@addto@macro\choicestext{ § ##1}%
    }
}{%
    \begin{easylist}%
        \choicestext
    \end{easylist}%
}
\catcode`\§=12
\makeatother

\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}
  • So before defining choices? – Simon Kuang Mar 1 '15 at 0:46
  • @SimonKuang Unrelated to your question but you should never use \edef like this \edef\oldchoicestext{\choicestext} if the argument has "user supplied text" most latex constructs fail in an \edef You probably want to just append eh text without expanding it. – David Carlisle Mar 1 '15 at 0:46
  • @SimonKuang where I had it, in this case before defining question – David Carlisle Mar 1 '15 at 0:47
  • What would you suggest in place of \edef to concatenate? – Simon Kuang Mar 1 '15 at 0:50
  • @SimonKuang for concatenation you could use \g@addto@macro (defined in the format) or better documented perhaps the etoolbox commands such as \appto, but I don't think you need concatenation at all here you are just concatenating a string that you immediately pass to easylist to split up back into the parts you concatenated. See my updated answer. – David Carlisle Mar 1 '15 at 0:55
3

You're forgetting that TeX uses macro expansion! And you already have the macro \choice that you can define in whatever way you want!

So we can define \choice to supply the easylist marker in front of the question text; the only precaution is to make the marker into an active character:

\documentclass[11pt]{memoir}

\usepackage{fontspec}
\usepackage{easylist}

\begingroup\lccode`~=`§
\lowercase{\endgroup\def\easylistmarker{~ }}
\newcommand{\choice}[1]{\easylistmarker#1} 

\newenvironment{question}  
  {\begin{easylist}}
  {\end{easylist}}

\begin{document}

\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}

\end{document}

enter image description here

If you want to go the long way with the substitutions (but it's unclear why), you can use a similar approach: use macro substitutions!

\documentclass[11pt]{memoir}

\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage{xstring}
\usepackage{easylist}
\usepackage{ifthen}

\begingroup\catcode`§=\active
\gdef\dosubstitutions{%
  \noexpandarg
  \expandafter\StrSubstitute\expandafter{\finchoicestext}{CHOICE}{ § }[\tempctext]%
}
\endgroup

\newenvironment{question}{%
    \newcommand{\choicestext}{}%
    \newcommand{\choice}[1]{%
        \edef\oldchoicestext{\choicestext}
        \edef\newchoicetext{##1}
        % just append
        \renewcommand{\choicestext}{\oldchoicestext CHOICE\newchoicetext}
    }%
}{
    \edef\finchoicestext{\choicestext}%
    %wow http://goo.gl/jHcwNY thank you egreg
    \dosubstitutions
    \begin{easylist}
        \tempctext
    \end{easylist}
}

\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}

The problem is that once a token has been scanned by TeX, its category code is fixed, so you can't do the trick with \catcode`§=\active inside the definition of question, but outside of it and use a macro for putting it in.

Using \edef doesn't seem the best approach, however. Better guard against too deep expansion:

\documentclass[11pt]{memoir}

\usepackage{fontspec}
\usepackage{xstring}
\usepackage{easylist}
\usepackage{etoolbox}

\begingroup\lccode`~=`§
\lowercase{\endgroup\def\easylistmarker{~ }}

\newenvironment{question}
 {%
  \def\choicestext{}% initialize
  \def\choice##1{%
    \xappto\choicestext{\expandonce{\easylistmarker} \unexpanded{##1}}%
  }%
 }
 {%
  \begin{easylist}
  \choicestext
  \end{easylist}
 }

\begin{document}
\begin{question}
    \choice{First choice}
    \choice{Second choice}
\end{question}
\end{document}
  • Thank you! I like your last solution. However, my current project now requires that I move away from LuaLaTeX/fontspec. Is there a way that example can work with inputenc and PDFLaTeX? – Simon Kuang May 23 '15 at 0:02
  • @SimonKuang Do \usepackage[at]{easylist} and change § into @ in the code. – egreg May 23 '15 at 9:09

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.