动机:我正在研究一种非常个性化的练习/答案为我的同事准备的软件包。exercise
、exsheets
等软件包不能满足我的需要。
为了实现这一点,我创建了一个exercises
环境以及\question
命令\subquestion
。它们按顺序嵌套使用(请参阅下面的代码以了解示例用法)。
代码:以下代码编译(我知道,这是一个很长的MWE):
\documentclass{scrartcl}
\usepackage[shortlabels]{enumitem}
\usepackage{environ}
\usepackage{etoolbox}
\usepackage{xparse}
%% New lists to use with enumerations (enumitem)
\newlist{lquestion}{enumerate}{1}
\setlist[lquestion,1]{label=\textbf{\arabic*.}, labelsep=0mm, leftmargin=0mm, nosep, parsep=0mm, align=parleft, resume=lquestion}
\newlist{lsubquestion}{enumerate}{1}
%% NOTE: itemsep and parsep must be equal
\setlist[lsubquestion, 1]{label=\textbf{\alph*)}, labelsep=.8em, leftmargin=*, nosep, topsep=0mm, itemsep=1mm, parsep=1mm, resume=lsubquestion}
%% Define new environment `exercises` and new commands `\question` and `\subquestion`
\makeatletter
\newtoggle{cgexer@bool@hasquest}
\newtoggle{cgexer@bool@hassubquest}
\toggletrue{cgexer@bool@hasquest}
\toggletrue{cgexer@bool@hassubquest}
%% Environment `exercises`
\NewDocumentEnvironment{exercises}{s O{}}
{%% Before
\global\togglefalse{cgexer@bool@hasquest}%% NO QUESTIONS YET
}%
{%% After
\iftoggle{cgexer@bool@hasquest}%
{%% THE EXERCISE BLOCK CONTAINS QUESTIONS...
\end{lquestion}
}{}%
}
%% Used within command `\question` to check whether text is provided before the first `\subquestion`
\newtoggle{cgexer@bool@hasgeneralquest}
\NewEnviron{preparegeneralquest}{%
%% Was text specified within `\question{...}`, before the first `\subquestion`?
\ifdefempty{\BODY}{}{\global\toggletrue{cgexer@bool@hasgeneralquest}}
\BODY%% Show the specified text
}
%% Command `\question`
\NewDocumentCommand{\question}{s O{} +g +g}{%
\global\togglefalse{cgexer@bool@hasgeneralquest}%% NO TEXT (GENERAL QUESTION) SPECIFIED BEFORE FIRST `\subquestion` YET
\global\togglefalse{cgexer@bool@hassubquest}%% NO `\subquestion`s YET
\nottoggle{cgexer@bool@hasquest}%
{%% FIRST QUESTION...
\begin{lquestion}[start=1]
\global\toggletrue{cgexer@bool@hasquest}%% THE EXERCISE BLOCK NOW CONTAINS 1+ QUESTION(S)
}{}%
%
\vspace{12pt plus 2.0pt minus 1.5pt}
\item START%% ***REPLACE WITH: \item \begin{preparegeneralquest}%
#3%% Show the content of the question (including all `\subquestion`s)
%
\iftoggle{cgexer@bool@hassubquest}%
{%% THE QUESTION CONTAINS SUB-QUESTIONS...
\end{lsubquestion}
}%
{%% THE QUESTION DOES NOT CONTAIN SUB-QUESTIONS...
STOP%% ***REPLACE WITH: \end{preparegeneralquest}%% To close \begin{preparegeneralquest}
}%
}
%% Command `\subquestion`
\NewDocumentCommand{\subquestion}{s O{} +g +g}{%
\nottoggle{cgexer@bool@hassubquest}%
{%% FIRST SUB-QUESTION...
STOP%% ***REPLACE WITH: \end{preparegeneralquest}%% To close \begin{preparegeneralquest}
\begin{lsubquestion}[start=1]
\global\toggletrue{cgexer@bool@hassubquest}%% THE QUESTION NOW CONTAINS 1+ SUB-QUESTION(S)
}{}%
%
\item #3%
}
\makeatother
\begin{document}
\begin{exercises}
\question{Question 1
\subquestion{Sub-question A}
\subquestion{Sub-question B}{Sub-answer B}
\subquestion{Sub-question C}{Sub-answer C}
\subquestion{Sub-question D}
\subquestion{Sub-question E}{Sub-answer E}
}{Answer 1}
\question{Question 2}
\question{Question 3}{Answer 3}
\question{Question 4
\subquestion{Sub-question A}{Sub-answer A}
\subquestion{Sub-question B}
\subquestion{Sub-question C}
\subquestion{Sub-question D}{Sub-answer D}
\subquestion{Sub-question E}
}{Answer 4}
\question{
\subquestion{Sub-question A}{Sub-answer A}
\subquestion{Sub-question B}
\subquestion{Sub-question C}
\subquestion{Sub-question D}{Sub-answer D}
\subquestion{Sub-question E}
}{Answer 4}
\end{exercises}
\vspace{2\baselineskip}
\makeatletter
Testing the \verb|preparegeneralquest| environment:
\textbf{Test 1}:
\togglefalse{cgexer@bool@hasgeneralquest}
\begin{preparegeneralquest}
This one contains text.
\end{preparegeneralquest}
(\textbf{\iftoggle{cgexer@bool@hasgeneralquest}{Text was provided}{No text provided.}})
\textbf{Test 2}:
\togglefalse{cgexer@bool@hasgeneralquest}
\begin{preparegeneralquest}
\end{preparegeneralquest}
(\textbf{\iftoggle{cgexer@bool@hasgeneralquest}{Text was provided}{No text provided.}})
\makeatother
\end{document}
输出:生成的输出是......
问题:由于布局原因,我必须确定在遇到\question{...}
第一个文本之前是否已经指定了某些文本(如果有)。\subquestion
为了实现这一点,我尝试使用environ
包以及和\NewEnviron
命令\BODY
来收集文本并检查(使用名为的新环境preparegeneralquest
)它是否为空或未使用\ifdefempty
(我尝试使用ifmtarg
包,但它在这种情况下失败,我不知道原因;可能是由于扩展?)。
现在,由于\subquestion
命令嵌套在\question
命令中,我尝试通过以下方式执行此操作强制插入 \begin{preparegeneralquest}
并在结构中的适当位置。这是通过更改上面提供的代码中用\end{preparegeneralquest}
标识的三行来实现的,即通过更改*** REPLACE WITH:
\item START
为了
\item \begin{preparegeneralquest}%
和(两次,因为它出现在两个不同的地方)
STOP
为了
\end{preparegeneralquest}
但是,这样做之后,文档编译失败,并给出错误消息:
\begin{preparegeneralquest} on input line 92 ended by \end{exercises}
[...]
! Undefined control sequence.
\env@preparegeneralquestion@parse ...name #1\env@nil
\csname env@preparegeneral...
等等。
问题:我做错了什么,有没有好的方法来追踪正在发生的事情(例如通过 .tmp 文件)?使用上面版本的代码,和START
文本STOP
在适当的位置排版,但我知道当此文本针对环境进行更改时,这可能是一个嵌套问题。
注意:
- 我不介意使用命令
\preparegeneralquest
而不是环境,但随后我会遇到包围问题,而我似乎无法使用\begingroup
and\endgroup
(或者更可怕的\bgroup
andegroup
)来解决这些问题。 - 如果您想知道为什么我要创建新命令
\question
而\subquestion
不是简单地将它们定义为列表enumitem
,那是因为它们将具有各种参数,包括使用基于键的选项列表pgfkeys
。