我想了解是什么导致下面的操作不起作用,以及如何修复它以便在每次调用命令时获得所需的输出\myQFormat
。
\documentclass[addpoints]{exam}
\usepackage{xparse}
\usepackage[xparse]{tcolorbox}
\renewcommand{\questionshook}{%
\setlength{\leftmargin}{0pt}%
\setlength{\labelwidth}{-\labelsep}%
}
\NewTColorBox{MarksTCBox} { O{} }{
left skip= 0pt,
right skip=0pt,
left=2pt,
right=2pt,
capture=hbox,
halign=center,
valign=center,
boxrule=0pt,
arc=0pt,
top=2pt,
bottom=2pt,
boxsep=0pt,
nobeforeafter,
box align = base,
baseline=4pt,
#1
}
\ExplSyntaxOn
\keys_define:nn { Qoptions }
{
label .tl_set:N = \l__Qoptions_label_tl,
label .initial:n = Question,
sublabel .tl_set:N = \l__Qoptions_sublabel_tl,
}
\cs_new:Npn \Question_header:n #1
{
\qformat{
\textbf{
\underline{
\large \tl_if_blank:nTF {\l__Qoptions_label_tl} { Question } { \l__Qoptions_label_tl }~
(\thequestion)\ \IfValueT{\l__Qoptions_sublabel_tl}{[\l__Qoptions_sublabel_tl]\ }
\begin{MarksTCBox}
\scan_stop: [\totalpoints\ Marks]
\end{MarksTCBox}
}
}
}
}
\NewDocumentCommand { \myQFormat } { O{} }
{
\group_begin:
\keys_set:nn { Qoptions } { #1 }
\Question_header:n { #1 }
\group_end:
}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\myQFormat
\question[15]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 1 [15 Marks]''
\myQFormat[label = Part]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 2 [10 Marks]''
\myQFormat[label=Part, sublabel=Subtitle]
\question[5]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 3 [Subtitle] [5 Marks]''
\myQFormat[sublabel=Subtitle]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 4 [Subtitle] [10 Marks]''
\end{questions}
\end{document}
答案1
主要问题有:
\tl_if_blank:nTF
需要一个带括号的“普通参数”(n
类型),直接包含要测试的标记,例如\tl_if_blank:nTF { abc~def } { true } { false }
,如果我们在宏定义中,并且参数#1
是标记列表,则\tl_if_blank:nTF {#1} { true } { false }
。但你在这里想要测试的是标记列表变量的内容(值)。为此,你需要\tl_if_blank:VTF
(V
原因价值传递给基本形式的第一个参数\tl_if_blank:nTF
)。 例如:\tl_if_blank:VTF \l__my_var_tl { true } { false }
当你的
\myQFormat
已完全执行时,它\group_end:
已将两个标记列表变量\l__Qoptions_label_tl
和恢复\l__Qoptions_sublabel_tl
为组开始之前的值,即此处为空。因此,当\question
使用这些变量作为问题格式的一部分时,它们都是空的。
对于第二点,以下代码\keys_set:nn
在的参数中调用。这允许您在格式定义中\qformat
使用名称\l__Qoptions_label_tl
和。我将在下面展示另一种可能的方法。\l__Qoptions_sublabel_tl
正如 egreg 所指出的,\tl_if_blank:...
标签的测试实际上并不是必需的,因为的初始值\l__Qoptions_label_tl
是用设置的label .initial:n = { Question }
(可以省略括号,因为中没有逗号Question
)。此测试唯一有用的情况是,当您希望在使用选项label=
或 even时获得“Question”作为标签时label={ }
(实际上,“blank”在此上下文中表示“空或仅空格”,因此,如果您传递仅包含空格标记的参数,\tl_if_blank:nTF
将执行“true”分支)。
\documentclass[addpoints]{exam}
\usepackage{xparse}
\usepackage[xparse]{tcolorbox}
\renewcommand{\questionshook}{%
\setlength{\leftmargin}{0pt}%
\setlength{\labelwidth}{-\labelsep}%
}
\NewTColorBox{MarksTCBox} { O{} }{
left skip= 0pt,
right skip=0pt,
left=2pt,
right=2pt,
capture=hbox,
halign=center,
valign=center,
boxrule=0pt,
arc=0pt,
top=2pt,
bottom=2pt,
boxsep=0pt,
nobeforeafter,
box align = base,
baseline=4pt,
#1,
}
\ExplSyntaxOn
\keys_define:nn { Qoptions }
{
label .tl_set:N = \l__Qoptions_label_tl,
label .initial:n = { Question },
sublabel .tl_set:N = \l__Qoptions_sublabel_tl,
}
\cs_new_protected:Npn \Qoptions_question_header:n #1
{
\qformat
{
% \question appears to create a group, so the options are duly cleared
% when the question title has been typeset.
\keys_set:nn { Qoptions } {#1}
\textbf
{
\underline
{
\large
% Possible but not really needed (see above):
% \tl_if_blank:VTF \l__Qoptions_label_tl { Question }
% { \l__Qoptions_label_tl }
\tl_use:N \l__Qoptions_label_tl
\nobreakspace \thequestion \
\tl_if_blank:VF \l__Qoptions_sublabel_tl
{ [ \l__Qoptions_sublabel_tl ] \ }
\begin{MarksTCBox}
\scan_stop: [\totalpoints\ Marks]
\end{MarksTCBox}
}
}
\hfill % Otherwise, you'll have an Underfull \hbox for each question.
}
}
\NewDocumentCommand { \myQFormat } { O{} }
{
\Qoptions_question_header:n {#1}
}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\myQFormat
\question[15]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 1 [15 Marks]''
\myQFormat[label = Part]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 2 [10 Marks]''
\myQFormat[label=Part, sublabel=Subtitle]
\question[5]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 3 [Subtitle] [5 Marks]''
\myQFormat[sublabel=Subtitle]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 4 [Subtitle] [10 Marks]''
\end{questions}
\end{document}
其他方法
避免因过早恢复\l__Qoptions_label_tl
和的值(在使用它们之前)而导致问题的另一种方法如下。您可以通过\l__Qoptions_sublabel_tl
\question
值of\l__Qoptions_label_tl
和\l__Qoptions_sublabel_tl
to\Qoptions_question_header:n
使用V
参数类型。由于您有两个“正常参数”要传递,因此该函数现在必须命名\Qoptions_question_header:nn
并接受两个参数#1
和#2
(标签和子标签)。然后您可以使用\cs_generate_variant:Nn \Qoptions_question_header:nn { VV }
创建所需的函数变体(即\Qoptions_question_header:VV
),并调用\Qoptions_question_header:VV \l__Qoptions_label_tl \l__Qoptions_sublabel_tl
来\myQFormat
传递值的\l__Qoptions_label_tl
和\l__Qoptions_sublabel_tl
到\Qoptions_question_header:nn
。
\documentclass[addpoints]{exam}
\usepackage{xparse}
\usepackage[xparse]{tcolorbox}
\renewcommand{\questionshook}{%
\setlength{\leftmargin}{0pt}%
\setlength{\labelwidth}{-\labelsep}%
}
\NewTColorBox{MarksTCBox} { O{} }{
left skip= 0pt,
right skip=0pt,
left=2pt,
right=2pt,
capture=hbox,
halign=center,
valign=center,
boxrule=0pt,
arc=0pt,
top=2pt,
bottom=2pt,
boxsep=0pt,
nobeforeafter,
box align = base,
baseline=4pt,
#1,
}
\ExplSyntaxOn
\keys_define:nn { Qoptions }
{
label .tl_set:N = \l__Qoptions_label_tl,
label .initial:n = { Question },
sublabel .tl_set:N = \l__Qoptions_sublabel_tl,
}
\cs_new_protected:Npn \Qoptions_question_header:nn #1#2
{
\qformat
{
\textbf
{
\underline
{
\large
% Possible but not really needed (see above):
% \tl_if_blank:nTF {#1} { Question } {#1}
#1 \nobreakspace \thequestion \
\tl_if_blank:nF {#2} { [ #2 ] \ }
\begin{MarksTCBox}
\scan_stop: [\totalpoints\ Marks]
\end{MarksTCBox}
}
}
\hfill % Otherwise, you'll have an Underfull \hbox for each question.
}
}
\cs_generate_variant:Nn \Qoptions_question_header:nn { VV }
\NewDocumentCommand { \myQFormat } { O{} }
{
\group_begin:
\keys_set:nn { Qoptions } {#1}
\Qoptions_question_header:VV \l__Qoptions_label_tl \l__Qoptions_sublabel_tl
\group_end:
}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\myQFormat
\question[15]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 1 [15 Marks]''
\myQFormat[label = Part]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 2 [10 Marks]''
\myQFormat[label=Part, sublabel=Subtitle]
\question[5]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 3 [Subtitle] [5 Marks]''
\myQFormat[sublabel=Subtitle]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 4 [Subtitle] [10 Marks]''
\end{questions}
\end{document}
与上面相同的输出。
答案2
当你调用时\tl_if_blank:nTF { \l__Qoptions_label_tl }
你总是会得到错误的结果,因为参数不为空。
使用新方法您只需执行\tl_use:N \l__Qoptions_label_tl
,因为变量将始终包含所需的字符串:初始值或使用键指定的内容label
。
使用\IfValueT
很好仅有的如果您传递了特定参数。使用新方法,您可以测试sublabel
变量中存储的值是否为空(这意味着尚未设置)。您可以使用测试键是否sublabel
已收到值\tl_if_blank:V(TF)
(括号表示您可以使用T
,F
或TF
并提供适当数量的参数)。
\documentclass[addpoints]{exam}
\usepackage{xparse}
\usepackage[xparse]{tcolorbox}
\renewcommand{\questionshook}{%
\setlength{\leftmargin}{0pt}%
\setlength{\labelwidth}{-\labelsep}%
}
\NewTColorBox{MarksTCBox} { O{} }{
left skip= 0pt,
right skip=0pt,
left=2pt,
right=2pt,
capture=hbox,
halign=center,
valign=center,
boxrule=0pt,
arc=0pt,
top=2pt,
bottom=2pt,
boxsep=0pt,
nobeforeafter,
box align = base,
baseline=4pt,
#1,
}
\ExplSyntaxOn
\keys_define:nn { Qoptions }
{
label .tl_set:N = \l__Qoptions_label_tl,
label .initial:n = Question,
sublabel .tl_set:N = \l__Qoptions_sublabel_tl,
}
\cs_new_protected:Npn \Qoptions_question_header:n #1
{
\qformat
{
% \question appears to create a group, so the options are duly cleared
% when the question title has been typeset.
\keys_set:nn { Qoptions } {#1}
\textbf
{
\underline
{
\large
\tl_use:N \l__Qoptions_label_tl
\c_space_tl
\thequestion
\c_space_tl
\tl_if_blank:VF \l__Qoptions_sublabel_tl
{ [ \l__Qoptions_sublabel_tl ] \c_space_tl }
\begin{MarksTCBox}
\scan_stop: [\totalpoints\ Marks]
\end{MarksTCBox}
}
}
\hfill % Otherwise, you'll have an Underfull \hbox for each question.
}
}
\NewDocumentCommand { \myQFormat } { O{} }
{
\Qoptions_question_header:n {#1}
}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\myQFormat
\question[15]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 1 [15 Marks]''
\myQFormat[label = Part]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 2 [10 Marks]''
\myQFormat[label=Part, sublabel=Subtitle]
\question[5]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Part 3 [Subtitle] [5 Marks]''
\myQFormat[sublabel=Subtitle]
\question[10]\hspace*{0pt}\vspace*{\baselineskip}
The output should be ``Question 4 [Subtitle] [10 Marks]''
\end{questions}
\end{document}