我正在尝试编写一个宏,它可以接受一组选择,然后列出它们(以特定但可能随机的顺序),以尽可能少地使用垂直空间,同时仍能很好地呈现。我想出了一个解决方案,但它使用了 hbox,因此如果单个给定的选择超过一行文本,就会失败。我的 MWE:
\documentclass{article}
\usepackage{forloop}
\makeatletter
\newcounter{choiceNum}
\newcounter{choiceEnvNum}
\newcounter{Iteration@printChoices}
\newenvironment{choices}{% Start environment code for multiple choice Choices environment
\setcounter{choiceNum}{0}% New MCChoices environment means a new set of choices, so reset current choice to 0.
\refstepcounter{choiceEnvNum}
}% End of start-environment code.
{% Start of end-environment code.
\printChoices
}% End of end-environment code.
\newcommand{\choice}[1]{% Choice command holds a multiple choice option by generating a new command with unique name that will be used to write the outputs at the end of the MCChoices environment.
\refstepcounter{choiceNum}% A new choice means we need to step current choice (before definition since counters starts at 0)
\expandafter\def\csname \Roman{choiceEnvNum}choice\roman{choiceNum}\endcsname{#1}
}% End of \choice code.
\providecommand{\printChoices}{% \printChoices is an internal macro to print the choices at the end of a MCChoice environment, depending on if we want them shuffled or not. Note: I removed the shuffle content as this is a mwe
% Now print the results:
\stepcounter{choiceNum}% Step counter for the < symbol in foreloop.
\forloop{Iteration@printChoices}{1}{\arabic{Iteration@printChoices}<\arabic{choiceNum}}{% Begin forloop
\hspace*{0pt}\hfill\hbox{\expandafter\choiceLetter{\arabic{Iteration@printChoices}} % Print the correct letter of the choice. Note that \hspace*{0pt} forces the \hfill to populate correctly, or else TeX will ignore it at the start of lines which makes everything look super weird.
\csname \Roman{choiceEnvNum}choice\roman{Iteration@printChoices}\endcsname}\hfill% Print choice
}% End forloop
% Check to see if we have a "last choice".
\ifcsname\Roman{choiceEnvNum}lastChoice\endcsname% If we do, then we should print it.
\hspace*{0pt}\hfill\hbox{\expandafter\choiceLetter{\arabic{choiceNum}} % Since we stepped choiceNum this hack will work.
\csname \Roman{choiceEnvNum}lastChoice\endcsname}\hfill% Print lastChoice
\fi%
}% End of \printChoices code.
\providecommand{\choiceLetter}[1]{% A simple internal command to print the correct letter.
\ifnum\numexpr#1\relax=1 A)\fi
\ifnum\numexpr#1\relax=2 B)\fi
\ifnum\numexpr#1\relax=3 C)\fi
\ifnum\numexpr#1\relax=4 D)\fi
\ifnum\numexpr#1\relax=5 E)\fi
\ifnum\numexpr#1\relax>5 ?Too many choices?)\fi
}% End \choiceLetter code.
\providecommand{\lastChoice}[1]{% This is a \choice command, but it will be forced to be printed last.
\expandafter\def\csname \Roman{choiceEnvNum}lastChoice\endcsname{\noexpand{#1}}
}% End of \lastChoice command.
\begin{document}
\begin{choices}
\choice{This is choice 1}
\choice{This is choice 2}
\choice{This is choice 3}
\choice{And this is some really long choice that will not fit on one line, which is why hbox really isn't a great plan because I may have someone crazy try to do something like this even though they really probably shouldn't!}
\choice{Oh, and we need math, so $\frac{x}{2} \cdot 15 = $ ?}
\end{choices}
\end{document}
这只是我想出的解决方案,但我希望有某种(相对)完美分离的水平扩展,最多 5 个选项,只有当单个\choice
内容占据整行时才会换行。我无法指定宽度为一定量,因为单个长选项会变成垂直列,而真正的目标是最小化垂直扩展。因此,我使用 hbox 来避免换行,但显然有些情况下换行是必要的,或者内容超出了纸张的一侧。
有什么建议么?
答案1
这或许就是您所寻找的东西?
\documentclass{article}
\usepackage{forloop}
\makeatletter
\newcounter{choiceNum}
\newcounter{choiceEnvNum}
\newcounter{Iteration@printChoices}
\newenvironment{choices}{% Start environment code for multiple choice Choices environment
\setcounter{choiceNum}{0}% New MCChoices environment means a new set of choices, so reset current choice to 0.
\refstepcounter{choiceEnvNum}
}% End of start-environment code.
{% Start of end-environment code.
\printChoices
}% End of end-environment code.
\newcommand{\choice}[1]{% Choice command holds a multiple choice option by generating a new command with unique name that will be used to write the outputs at the end of the MCChoices environment.
\refstepcounter{choiceNum}% A new choice means we need to step current choice (before definition since counters starts at 0)
\expandafter\def\csname \Roman{choiceEnvNum}choice\roman{choiceNum}\endcsname{#1}
}% End of \choice code.
\providecommand{\printChoices}{% \printChoices is an internal macro to print the choices at the end of a MCChoice environment, depending on if we want them shuffled or not. Note: I removed the shuffle content as this is a mwe
% Now print the results:
\stepcounter{choiceNum}% Step counter for the < symbol in foreloop.
\newdimen\qb@xlen \qb@xlen 0pt
\forloop{Iteration@printChoices}{1}{\arabic{Iteration@printChoices}<\arabic{choiceNum}}{% Begin forloop
\parindent=0pt
\newbox\ch@icebox
\setbox\ch@icebox\hbox{\expandafter\choiceLetter{\arabic{Iteration@printChoices}} % Print the correct letter of the choice. Note that \hspace*{0pt} forces the \hfill to populate correctly, or else TeX will ignore it at the start of lines which makes everything look super weird.
\csname \Roman{choiceEnvNum}choice\roman{Iteration@printChoices}\endcsname }
\ifdim \wd\ch@icebox > \textwidth
% Too wide!
\hfill \par \unhbox\ch@icebox \hfill \par
\qb@xlen=0pt
\else
\advance\qb@xlen by \wd\ch@icebox
\ifdim \qb@xlen > \textwidth
\par \hfill \box\ch@icebox \hfill
\qb@xlen=\wd\ch@icebox
\else
\hfill \box\ch@icebox \hfill
\fi
\fi
% \hfill\hbox{\expandafter\choiceLetter{\arabic{Iteration@printChoices}} % Print the correct letter of the choice. Note that \hspace*{0pt} forces the \hfill to populate correctly, or else TeX will ignore it at the start of lines which makes everything look super weird.
% \csname \Roman{choiceEnvNum}choice\roman{Iteration@printChoices}\endcsname}\hfill% Print choice
}% End forloop
% Check to see if we have a "last choice".
\ifcsname\Roman{choiceEnvNum}lastChoice\endcsname% If we do, then we should print it.
\hspace*{0pt}\hfill\hbox{\expandafter\choiceLetter{\arabic{choiceNum}} % Since we stepped choiceNum this hack will work.
\csname \Roman{choiceEnvNum}lastChoice\endcsname}\hfill% Print lastChoice
\fi%
}% End of \printChoices code.
\providecommand{\choiceLetter}[1]{% A simple internal command to print the correct letter.
\ifnum\numexpr#1\relax=1 A)\fi
\ifnum\numexpr#1\relax=2 B)\fi
\ifnum\numexpr#1\relax=3 C)\fi
\ifnum\numexpr#1\relax=4 D)\fi
\ifnum\numexpr#1\relax=5 E)\fi
\ifnum\numexpr#1\relax>5 ?Too many choices?)\fi
}% End \choiceLetter code.
\providecommand{\lastChoice}[1]{% This is a \choice command, but it will be forced to be printed last.
\expandafter\def\csname \Roman{choiceEnvNum}lastChoice\endcsname{\noexpand{#1}}
}% End of \lastChoice command.
\begin{document}
\begin{choices}
\choice{This is choice 1}
\choice{This is choice 2}
\choice{This is choice 3, it can't fit with A and B}
\choice{And this is some really long choice that will not fit on one line, which is why hbox really isn't a great plan because I may have someone crazy try to do something like this even though they really probably shouldn't!}
\choice{Oh, and we need math, so $\frac{x}{2} \cdot 15 = $ ?}
\end{choices}
\end{document}
答案2
这是 Gleason 答案的双框版本。我还参与了许多不必要的更改和一些真正必要的更改(例如不将\newbox
或\newdimen
放在循环内)。
一个关键的附加功能是\choicesep
(胶水),它既可以扩展\hfill
,又可以提供最小 2em 的间隙。
\documentclass{article}
\usepackage{forloop}
\makeatletter
\newcounter{choiceNum}
\newcounter{choiceEnvNum}
\newcounter{Iteration@printChoices}
\renewcommand{\thechoiceEnvNum}{\Roman{choiceEnvNum}}
\renewcommand{\thechoiceNum}{\Alph{choiceNum}}
\renewcommand{\theIteration@printChoices}{\Alph{Iteration@printChoices}}
\newsavebox\choicebox
\newskip\choicesep% separation between choices (glue)
\choicesep=2em plus 100fil
\newenvironment{choices}{% Start environment code for multiple choice Choices environment
\setcounter{choiceNum}{0}% New MCChoices environment means a new set of choices, so reset current choice to 0.
\refstepcounter{choiceEnvNum}%
}% End of start-environment code.
{% Start of end-environment code.
\printChoices
}% End of end-environment code.
\newcommand{\choice}[1]{% Choice command holds a multiple choice option by generating a new command with unique name that will be used to write the outputs at the end of the MCChoices environment.
\refstepcounter{choiceNum}% A new choice means we need to step current choice (before definition since counters starts at 0)
\expandafter\def\csname \thechoiceEnvNum choice\roman{choiceNum}\endcsname{#1}%
\ignorespaces
}% End of \choice code.
\providecommand{\printChoices}{% \printChoices is an internal macro to print the choices at the end of a MCChoice environment, depending on if we want them shuffled or not. Note: I removed the shuffle content as this is a mwe
% Now print the results:
\stepcounter{choiceNum}% Step counter for the < symbol in foreloop.
\savebox\choicebox{}%
\parindent=0pt
\forloop{Iteration@printChoices}{1}{\value{Iteration@printChoices}<\value{choiceNum}}{% Begin forloop
\sbox0{\theIteration@printChoices\space
\csname \thechoiceEnvNum choice\roman{Iteration@printChoices}\endcsname}%
\ifdim \textwidth > \dimexpr \wd\choicebox+\wd0\relax
% okay
\ifdim \wd\choicebox>0pt
\savebox\choicebox{\unhbox\choicebox \hspace{\choicesep}\unhbox0}%
\else
\setbox\choicebox=\box0
\fi
\else% too wide
\ifdim \wd\choicebox>0pt
\par
\hangindent=1em
\unhbox\choicebox
\fi
\setbox\choicebox=\box0
\fi}%
\ifdim \wd\choicebox>0pt %last box
\par
\hangindent=1em
\unhbox\choicebox
\par
\fi
}% End of \printChoices code.
\begin{document}
\begin{choices}
\choice{This is choice 1}
\choice{This is choice 2}
\choice{This is choice 3, it can't fit with A and B}
\choice{And this is some really long choice that will not fit on one line, which is why hbox really isn't a great plan because I may have someone crazy try to do something like this even though they really probably shouldn't!}
\choice{Oh, and we need math, so $\frac{x}{2} \cdot 15 = $ ?}
\end{choices}
\end{document}