自定义考试积分表

自定义考试积分表

我正在尝试为考试创建自定义分数表。目前,我们部门的讲师使用 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}

我所能做的是制作一个包含几个问题的文档,后面跟着一个评分表,然后显示表格下方的值和分数。表格下方的值仅用于测试目的,不会出现在最终产品中。目前表格右侧的垂直线缺失,但那只是因为我还没有完成每一行。 考试成绩表样本 v2

因此,我认为我的下一步是找到标签和值列表的长度,将其除以“一半”(如果问题/部分的数量是奇数,我想让 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}

相关内容