我正在尝试为考试创建自定义分数表。目前,我们部门的讲师使用 MS Word 编写这些考试,因此为了减轻成长的烦恼,我希望尽可能地模仿旧设计。
我知道考试包中有一个内置成绩表,但格式不同。此外,对于包含子部分的问题(例如 1a-1h),无法直接访问每个部分的分值(考试包中有\pointsofquestion{#}
,但它仅引用顶层)。
理想情况下,我希望能够自动计算多项选择题和自由回答题的总分,并填充表格的“可能得分”列。自动生成表格似乎有点太牵强(行数可能有所不同),但我也不反对为此提供解决方案。
编辑1:
正如评论中提到的,我一直在破解引用答案中发布的代码。目前,我能够获取问题部分的所有标签以及分值来创建表格的代码。下面是获取标签和分值的 MWE。我已经删除了构建表格的具体代码,因为我现在可以相当轻松地弄清楚该语法。
\documentclass[addpoints]{exam}
\usepackage{xparse,xpatch}
% redefine \question command to be \myquest
\appto\questions{\let\examquest\question\let\question\myquest}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\ExplSyntaxOn
\tl_new:N \g_grade_list_tl % this is a grading list
\int_new:N \g_mcscore_int% this will be the multiple choice score
\int_new:N \g_frscore_int% this will be the free response score
\int_new:N \g_exscore_int% this will be the total exam score
%% Add question parts to grading list
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
% don't do anything special inside solutions
\if@insolution\exampart[#1]
\else\exampart[#1]
\int_gadd:Nn \g_frscore_int {#1}
\tl_gput_right:Nx \g_grade_list_tl {\arabic{question}\alph{partno},}
\tl_gput_right:No \g_grade_list_tl {#1,}
\fi
}
}
\NewDocumentCommand\prtGradeList{}{
\tl_use:N \g_grade_list_tl}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\question
\begin{parts}
\part[1]
\part[2]
\end{parts}
\question
\begin{parts}
\part[4]
\part[2]
\end{parts}
\question
\begin{parts}
\part[2]
\part[4]
\part[4]
\part[1]
\end{parts}
\question[5]
\end{questions}
\prtGradeList{}
\end{document}
我所能做的是制作一个包含几个问题的文档,后面跟着一个评分表,然后显示表格下方的值和分数。表格下方的值仅用于测试目的,不会出现在最终产品中。目前表格右侧的垂直线缺失,但那只是因为我还没有完成每一行。
因此,我认为我的下一步是找到标签和值列表的长度,将其除以“一半”(如果问题/部分的数量是奇数,我想让 RHS 更长)。
最后,如果我能把这个表格放在文档的第一页上,那就锦上添花了。我还没想好怎么做,因为我不确定是否应该将表格保存到外部文件或其他什么地方。
编辑2:
阅读完 expl3 文档并进行实验后,我尝试使用序列构建表:
\documentclass[addpoints]{exam}
\usepackage{xparse,xpatch,multirow}
\usepackage[table,xcdraw]{xcolor}
\definecolor{rowGray}{HTML}{EFEFEF}
\def\scantronPt{1} %% Scantron point
\def\numGradeCols{2}
% redefine \question command to be \myquest
\appto\questions{\let\examquest\question\let\question\myquest}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\makeatletter
\ExplSyntaxOn
\tl_new:N \g_grade_table_tl% this will; become the new grade table
\seq_new:N \g_grade_seq % this is a grading sequence
\int_new:N \g_mcscore_int% this will be the multiple choice score
\int_new:N \g_frscore_int% this will be the free response score
\int_new:N \g_exscore_int% this will be the total exam score
%% Add question parts to grading sequence
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
\if@insolution\exampart[#1]
\else\exampart[#1]
\int_gadd:Nn \g_frscore_int {#1}
\seq_gput_right:Nx \g_grade_seq {\arabic{question}\alph{partno}}
\seq_gput_right:No \g_grade_seq {#1}
\fi
}
}
%% Add question to grading sequence
\NewDocumentCommand\myquest{o}{
\IfNoValueTF{#1}{\examquest}{
\if@insolution\examquest[#1]
\else\examquest[#1]
\int_gadd:Nn \g_frscore_int {#1}
\seq_gput_right:Nx \g_grade_seq {\arabic{question}}
\seq_gput_right:No \g_grade_seq {#1}
\fi
}
}
\NewDocumentCommand\GradeTable{}{% the new grade table
%\BuildGradeTable{}
\seq_gput_right:Nn \g_grade_seq {Scantron}
\seq_gput_right:Nx \g_grade_seq {\scantronPt}
\seq_new:N \g_gradeLeft_seq
%% Macro
\def\seqLen{\seq_count:N \g_grade_seq}
\def\seqLeftLen{\seq_count:N \g_gradeLeft_seq}
%% Grab sequence original length
\int_const:Nn \seqOrigLen \seqLen
%% Create two integer variables
\int_new:N \leftSideLen \int_new:N \rightSideLen
%% Compute length of left and right columns
\int_gset:Nn \leftSideLen {\int_eval:n {2*\int_div_truncate:nn \seqOrigLen {4}}}
\int_gset:Nn \rightSideLen {\int_eval:n {\seqOrigLen-\int_use:N \leftSideLen}}
%% Split sequence in two
\int_do_until:nNnn {\seqLen} = {\int_use:N \rightSideLen} {
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\seq_gpush:Nx \g_gradeLeft_seq \l_tmpa_tl
}
\seq_reverse:N \g_gradeLeft_seq
%% Displays sequences in terminal (debugging purposes)
%\seq_show:N \g_gradeLeft_seq
%\seq_show:N \g_grade_seq
%% Build Table
\int_do_until:nNnn {\seqLeftLen} = {0} {
\tl_gput_right:Nn \g_grade_table_tl {\hline}
\seq_gpop:NN \g_gradeLeft_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl & }
\seq_gpop:NN \g_gradeLeft_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl &&}
\seq_gpop:NN \g_grade_seq \l_tmpb_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpb_tl &}
\seq_gpop:NN \g_grade_seq \l_tmpb_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpb_tl & \\}
}
\seq_if_empty:NF \g_grade_seq {
\tl_gput_right:Nn \g_grade_table_tl {\hline \multicolumn{2}{r}{}& }
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl &}
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl & \\}
}
%\seq_show:N \g_gradeLeft_seq
%\seq_show:N \g_grade_seq
\tl_show:N \g_grade_table_tl
\renewcommand{\arraystretch}{1.7}
%\tl_gput_right:Nn \g_grade_table_tl {\hline Scantron&\scantronPt &&\\}
\int_gadd:Nn \g_frscore_int {\scantronPt }
\int_gadd:Nn \g_mcscore_int {\g_frscore_int}
\tl_gclear:N \g_grade_table_tl
\begin{center}
\begin{tabular}{|*{6}{c|}} %% This syntax repeats column types
\multicolumn{6}{c}{\textit{\textbf{For~instructor~or~teaching~assistant~use~only.}}}\\[5pt]\hline
\rowcolor{rowGray}
\multicolumn{1}{|r|}{\textbf{Question}} & \multicolumn{1}{r|}{\textbf{Points~Possible}} & \multicolumn{1}{r|}{\textbf{Points~Earned}} & \textbf{Question} & \textbf{Points~Possible} & \textbf{Points~Earned}\\ \hline
%\tl_use:N \g_grade_table_tl \hline
\multicolumn{2}{r}{} & \multicolumn{2}{|r|}{\textbf{Multiple~Choice}} & \int_use:N \g_mcscore_int & \\ \cline{3-6}
\multicolumn{2}{l}{} & \multicolumn{2}{|r|}{\textbf{Free~Response}} & \int_use:N \g_frscore_int &\\ \cline{3-6}
\multicolumn{2}{l}{} & \multicolumn{2}{|r|}{\textit{\textbf{Exam~Total}}} & \int_use:N \g_exscore_int &\\ \cline{3-6}
\end{tabular}
\end{center}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\begin{questions}
\question
What if there were no air?
\begin{parts}
\part[1]
Describe the effect on the balloon industry.
\part[2]
Describe the effect on the aircraft industry.
\end{parts}
\question
\begin{parts}
\part[4]
Define the universe.
Give three examples.
\part[2]
If the universe were to end, how would you know?
\end{parts}
\question
\begin{parts}
\part[2]
\part[4]
\part[4]
\part[1]
\part[1]
\end{parts}
\question[5]
%\question[1]
\end{questions}
\GradeTable{}
\end{document}
此时,我可以看到\g_grade_table_tl
.log 文件中给出了我想要的内容,但是当我通过 pdflatex 运行此代码时,它会卡在函数处\GradeTable{}
。
答案1
由于您要将问题部分的分数放在两列中,并且分数的数量是可变的,因此您需要将分数存储在某个地方,然后在最后生成整个表格。基于我的先前的想法,我会把分数放到LaTeX3序列,同时跟踪总分和得分数量。正如我在另一篇文章中所说,我将定义一个\GradeBook
命令来生成自定义表格:
我不确定在使用时“多项选择题”和“自由回答题”是如何编码的考试这就是为什么我要求最小工作示例:),因此在下面的代码中我使用了作弊手段,对这些标记进行了硬编码:
\def\multiplechoice{54}
\def\freeresponse{46}
除此之外,一切都是自动的。以下是完整代码:
\documentclass[addpoints]{exam}
\usepackage[table]{xcolor}
\usepackage{xparse,xpatch}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\def\multiplechoice{54}
\def\freeresponse{46}
\makeatletter
\ExplSyntaxOn
% this will become a sequence of the part numbers and scores
% like: 1a&10&, 1b&8&, 1c&9&, 2a&6, 2b&8&, 3&12&, 4&14&, ...
\seq_new:N \g_part_scores_seq
\tl_new:N \g_grade_table_tl
\int_new:N \g_total_score_int% this will be the exam score
\int_new:N \g_number_of_scores_int
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
% don't do anything special inside solutions
\if@insolution\exampart[#1]
\else\exampart[#1]
% store both the part number and score in \g_part_scores_seq
% together with their column separators for the tabular env
\tl_set:Nx \l_tmpa_tl { \arabic{question}\alph{partno} }
\tl_put_right:Nn \l_tmpa_tl {&}
\tl_put_right:No \l_tmpa_tl {#1}
\tl_put_right:Nn \l_tmpa_tl {&}
\seq_gput_right:No \g_part_scores_seq \l_tmpa_tl
% increment the running total and number of scores
\int_gadd:Nn \g_total_score_int {#1}
\int_gincr:N \g_number_of_scores_int
\fi
}
}
% print row #1 of the part scores in the grade table
\cs_new:Nn \__add_row_to_grade_table:n {
\tl_gput_right:Nx \g_grade_table_tl {\seq_item:Nn \g_part_scores_seq {#1}}
\tl_gput_right:Nn \g_grade_table_tl { & }
\tl_gput_right:Nx \g_grade_table_tl {\seq_item:Nn \g_part_scores_seq {#1+\g_number_of_scores_int/2}}
\tl_gput_right:Nn \g_grade_table_tl {\\\hline}
}
\NewDocumentCommand\GradeTable{}{% the new grade table
% we need an exam number of scores so add two
% empty cells if we have an odd number
\int_if_odd:nT {\g_number_of_scores_int} {
\seq_gput_right:Nn \g_part_scores_seq {&}
\int_ginc:N \g_number_of_scores_int
}
\int_gset:Nn \g_number_of_scores_int {\g_number_of_scores_int}
\int_gadd:Nn \g_total_score_int { \multiplechoice }
\int_gadd:Nn \g_total_score_int { \freeresponse }
% create the grade table
\tl_gclear:N \g_grade_table_tl
\int_step_function:nnN {1} {\g_number_of_scores_int/2} \__add_row_to_grade_table:n
\begin{tabular}{|c|c|c|c|c|c|}\hline\rowcolor{gray!20}
Question & Points~Possible & Points~Earned & Question & Points~Possible & Points~Earned \\\hline
\tl_use:N \g_grade_table_tl
\multicolumn2{c|}{}&\multicolumn{2}{r|}{Multiple~Choice}
& \multiplechoice & \\\cline{3-6}
\multicolumn2{c|}{}&\multicolumn{2}{r|}{Free~response}
& \freeresponse & \\\cline{3-6}
\multicolumn2{c|}{}&\multicolumn{2}{r|}{\textit{Exam~total}}
& \int_use:N \g_total_score_int & \\\cline{3-6}
\end{tabular}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\begin{questions}
\question
What if there were no air?
\begin{parts}
\part[4]
Describe the effect on the balloon industry.
\part[6]
Describe the effect on the aircraft industry.
\end{parts}
\question
\begin{parts}
\part[12]
Define the universe.
Give three examples.
\part[8]
If the universe were to end, how would you know?
\end{parts}
\end{questions}
\GradeTable
\end{document}
编辑
以下是更新内容(和流线型) 版本的上述代码将数据保存到辅助文件并将其读回以构建成绩表。这样,您可以将表格放在任何您喜欢的地方,包括在文档的开头,但也意味着您必须对文件进行两次 LaTeX 处理,才能看到任何分数。
在编译该文档两次或更多次之后,更新后的 MWE 给出以下输出:
这与之前大致相同,只是表格现在位于文档顶部。如果您只编译一次文档,则成绩表将没有单个问题或其部分的分数,总分将为 0。以下是更新后的代码:
\documentclass[addpoints]{exam}
\usepackage[table]{xcolor}
\usepackage{xparse,xpatch,etoolbox}
% redefine \question command to be \myquest
\appto\questions{\let\examquestion\question\let\question\myquestion}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\def\multiplechoice{5}
\def\freeresponse{6}
\makeatletter
\ExplSyntaxOn
% this will become a sequence of the part numbers and scores
% like: 1a,10,1b,8,1c,9,2a,6,2b,8,3,12,4,14, ...
\clist_new:N \g_grades_clist
\clist_new:N \g_grades_aux_clist
\int_new:N \g_row_int
\int_new:N \g_multiple_choice_int
\int_new:N \g_free_response_int
\int_new:N \g_grade_total_int
\int_new:N \g_number_of_scores_int
% add a question/part number and score to \g_grades_clist
\cs_new:Nn \__add_to_grades_list:nn {
\clist_gput_right:Nx \g_grades_clist { #1 }
\clist_gput_right:Nx \g_grades_clist { #2 }
}
\NewDocumentCommand\myquestion{o}{
\IfNoValueTF{#1}{\examquestion}{
% don't do anything special inside solutions
\if@insolution\examquestion[#1]
\else\examquestion[#1]
% store both the part number and score in \g_grades_clist
\__add_to_grades_list:nn { \arabic{question} } { #1 }
\fi
}
}
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
% don't do anything special inside solutions
\if@insolution\exampnrt[#1]
\else\exampart[#1]
% store both the part number and score in \g_grades_clist
\__add_to_grades_list:nn { \arabic{question}\alph{partno} } { #1 }
\fi
}
}
\AtEndDocument{
\iow_now:cx { @auxout } {
\token_to_str:N \SetGradeList { \g_grades_clist } ^^J
\token_to_str:N \SetMultipleChoice {\multiplechoice} ^^J
\token_to_str:N \SetFreeResponse {\freeresponse} ^^J
}
}
% set grade list, multiple choice and free responses from the aux file
\NewDocumentCommand\SetGradeList{m}{\clist_gset:Nn \g_grades_aux_clist {#1}}
\NewDocumentCommand\SetMultipleChoice{m}{\int_gset:Nn \g_multiple_choice_int {#1}}
\NewDocumentCommand\SetFreeResponse{m}{\int_gset:Nn \g_free_response_int {#1}}
% print row #1 of the part scores in the grade table
\cs_new:Nn \__add_row_to_grade_table: {
\int_gincr:N \g_row_int
\clist_item:Nn \g_grades_aux_clist {2*\g_row_int-1}
&\clist_item:Nn \g_grades_aux_clist {2*\g_row_int}
\int_gadd:Nn \g_grade_total_int {\clist_item:Nn \g_grades_aux_clist {2*\g_row_int}}
&&
\int_compare:nTF {2*\g_row_int+\g_number_of_scores_int <= \clist_count:N \g_grades_aux_clist }{
\clist_item:Nn \g_grades_aux_clist {2*\g_row_int+\g_number_of_scores_int-1}
&\clist_item:Nn \g_grades_aux_clist {2*\g_row_int+\g_number_of_scores_int}
\int_gadd:Nn \g_grade_total_int {\clist_item:Nn \g_grades_aux_clist {2*\g_row_int+\g_number_of_scores_int}}
}{&}
&\\\hline
\int_compare:nT {\g_row_int < \g_number_of_scores_int/2} { \__add_row_to_grade_table: }
}
\NewDocumentCommand\PrintGradeTable{}{% the new grade table
% we need an exam number of scores so add two
% empty cells if we have an odd number
\int_set:Nn \g_number_of_scores_int {(\clist_count:N \g_grades_aux_clist)/2}
\int_if_odd:nT {\g_number_of_scores_int} {
\int_add:Nn \g_number_of_scores_int {1}
}
\int_gzero:N \g_row_int % a counter to step through the rows
\int_add:Nn \g_grade_total_int { \g_multiple_choice_int }
\int_add:Nn \g_grade_total_int { \g_free_response_int }
% create the grade table
\begin{tabular}{|c|c|c|c|c|c|}\hline\rowcolor{gray!20}
Question & Points~Possible & Points~Earned & Question & Points~Possible & Points~Earned \\\hline
% the number of rows that we need is \g_number_of_scores_int/2
\int_compare:nT {\g_number_of_scores_int>0} { \__add_row_to_grade_table: }
\multicolumn2{c|}{}&\multicolumn{2}{r|}{Multiple~Choice}
& \int_use:N \g_multiple_choice_int & \\\cline{3-6}
\multicolumn2{c|}{}&\multicolumn{2}{r|}{Free~response}
& \int_use:N \g_free_response_int & \\\cline{3-6}
\multicolumn2{c|}{}&\multicolumn{2}{r|}{\textit{Exam~total}}
& \int_use:N \g_grade_total_int & \\\cline{3-6}
\end{tabular}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\PrintGradeTable
\begin{questions}
\question
\begin{parts}
\part[1]
\part[2]
\end{parts}
\question
\begin{parts}
\part[4]
\part[2]
\end{parts}
\question
\begin{parts}
\part[2]
\part[4]
\part[4]
\part[1]
\end{parts}
\question[5]
\end{questions}
\end{document}
代码可能比以前简单了。主要变化如下:
- 与新的 MWE 一样,我添加了
\myquestion
一个新命令\__add_to_grades_list:nn
,用于将问题/部分标签和分数添加到成绩列表中- 我现在不再使用序列,而是使用 clists(=逗号分隔列表),因为这与辅助文件配合使用效果更好
- 成绩数据保存到辅助文件中,然后读回
\g_grades_aux_clist
- 成绩表现已打印到位
- 我仍然对多项选择题和自由回答题的标记进行硬编码,但我添加了代码来将这些值保存到辅助文件中
- 新版本进行了徒手循环,以避免 overleaf 和 TeXLive 2019 尚未移植到 ubuntu 的潜在问题
编译该文档一次后,您将在 aux 文件中找到以下几行:
\SetGradeList{1a,1,1b,2,2a,4,2b,2,3a,2,3b,4,3c,4,3d,1,4,5}
\SetMultipleChoice{5}
\SetFreeResponse{6}
这是用于构建等级表的数据。
\SetFreeResponse{6}