检查环境内容的第一部分是否为空

检查环境内容的第一部分是否为空

动机:我正在研究一种非常个性化的练习/答案为我的同事准备的软件包。exerciseexsheets等软件包不能满足我的需要。

为了实现这一点,我创建了一个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而不是环境,但随后我会遇到包围问题,而我似乎无法使用\begingroupand \endgroup(或者更可怕的\bgroupand egroup)来解决这些问题。
  • 如果您想知道为什么我要创建新命令\question\subquestion不是简单地将它们定义为列表enumitem,那是因为它们将具有各种参数,包括使用基于键的选项列表pgfkeys

相关内容