如何扩展 \setcounter 中的宏?

如何扩展 \setcounter 中的宏?

我正在使用考试类来排版考试。作为 MWE,我的考试有一个 MCQ 部分和简答题部分。

在成绩表中,我希望 MCQ 部分显示为一行总分(而不是 20-30 个 1 分框),而 Short 部分按问题显示。由于没有预定义命令,我想编写自己的表格生成器:

\documentclass[addpoints]{exam}

\begin{document}

\newcounter{qnitr}
\setcounter{qnitr}{\firstqinrange{short}}
\begin{tabular}{|c|c|c|}
    \hline
    Qns & Points & Score \\\hline
    MCQ & \pointsinrange{mcq} & \\\hline
    \whiledo{ \not \( \theqnitr > \lastqinrange{short} \) }{%
        \ifthenelse{ \not \( \theqnitr > \lastqinrange{short} \) }{%
            \theqnitr & \pointsofquestion{\theqnitr} & \\ \hline
        }{blah}
        \stepcounter{qnitr}
    }
    Total: & \numpoints & \\\hline
\end{tabular}

\begin{questions}
    \section{MCQ Section}
    \begingradingrange{mcq}
        \question[2]
        MCQ Question 1

        \question[2]
        MCQ Question 2

        \question[2]
        MCQ Question 3
    \endgradingrange{mcq}

    \section{Short Answer Section}
    \begingradingrange{short}
        \question
        This question has 3 parts.
        \begin{parts}
            \part[2]
                Part 1.
            \part[2]
                Part 2.
            \part[3]
                Part 3
        \end{parts}

        \question
        This question has 2 parts.
            \begin{parts}
            \part[5]
                Part 1.
            \part[4]
                Part 2.
        \end{parts}
    \endgradingrange{short}
    \end{questions}
\end{document}

我想要实现的是这样的表格:

在此处输入图片描述

但是,\setcounter{qnitr}{\firstqinrange{short}}出现了一个错误。

我花了几个小时尝试查找要使用的扩展宏\setcounter,但似乎找不到解决方案。我了解到它\setcounter很脆弱,需要扩展为数字。但我尝试了 、 、 的各种组合,\noexpand由于我缺乏对 LaTeX 评估的了解和理解,我只是随机尝试。\protect\value

我查看了exam.cls表格生成方式的线索,找到了如下代码

\edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}%
\edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}%
\let\first@pq@index=\tbl@firstq
\let\last@pq@index=\tbl@lastq

...

\setcounter{num@cols}{\tbl@lastq}%
\addtocounter{num@cols}{-\tbl@firstq}%

为什么考试班能够为命令设置计数器,但当我尝试这样做时,它总是失败?

答案1

通过检查文件。使用和exam.cls生成的实际问题范围数量可以通过这两个宏调用,\firstqinrange\lastqinrange\range@short@firstq\range@short@lastq

因此代码应编辑如下:

\documentclass[addpoints]{exam}


\begin{document}

\newcounter{fqr}
\newcounter{lqr}
\makeatletter
\setcounter{fqr}{\range@short@firstq}
\setcounter{lqr}{\range@short@lastq}
\makeatother

\begin{tabular}{|c|c|c|}
    \hline
    Qns & Points & Score \\\hline
    MCQ & \pointsinrange{mcq} & \\\hline
    \whiledo{ \not \( \thefqr > \thelqr \) }{%
        \ifthenelse{
        \not \( \thefqr > \thelqr \) }{%
            \thefqr & \pointsofquestion{\thefqr} & \\ \hline%
        }{blah}%
        \stepcounter{fqr}%
    }
    Total: & \numpoints & \\\hline
\end{tabular}

\begin{questions}
    \section{MCQ Section}
    \begingradingrange{mcq}
        \question[2]
        MCQ Question 1

        \question[2]
        MCQ Question 2

        \question[2]
        MCQ Question 3
    \endgradingrange{mcq}

    \section{Short Answer Section}
    \begingradingrange{short}
        \question
        This question has 3 parts.
        \begin{parts}
            \part[2]
                Part 1.
            \part[2]
                Part 2.
            \part[3]
                Part 3
        \end{parts}

        \question
        This question has 2 parts.
            \begin{parts}
            \part[5]
                Part 1.
            \part[4]
                Part 2.
        \end{parts}
    \endgradingrange{short}
    \end{questions}

\end{document}

在此处输入图片描述

编辑:我会尽力回答你的问题。如果这里提到的任何内容不正确。请纠正我。问题来自命令\def\firstqinrange定义\lastqinrange。如果你看一下文件中这两个命令的定义exam.cls。例如,对于\firstqinrange

\newcommand{\firstqinrange}[1]{%
  \def\tbl@range{#1}% define a macro using #1, e.g. If this is the short range, #1=short.
  \@ifundefined{range@\tbl@range @firstq}% use previous macro in here to judge if command \range@short@firstq has not been defined.
  {\bad@range}% If true, return a error.
  {\csname range@#1@firstq\endcsname}% If false, return \range@short@firstq.
}% firstqinrange

在这个定义中,命令\def不可扩展。为什么?你可以看看这个邮政。因此,为了更好地说明这一点,我们可以修补该命令\firstqinrange\lastqinrange消除\def使用包的命令etoolbox

\documentclass[addpoints]{exam}

\usepackage{etoolbox}
\makeatletter
\patchcmd{\firstqinrange}{\def\tbl@range{#1}\@ifundefined{range@\tbl@range @firstq}}{\@ifundefined{range@#1@firstq}}{}{}

%% get rid of `\def\tbl@range{#1}` and using `range@#1@firstq` as value testing the define state.

\patchcmd{\lastqinrange}{\def\tbl@range{#1}\@ifundefined{range@\tbl@range @lastq}}{\@ifundefined{range@#1@lastq}}{}{}

%% get rid of `\def\tbl@range{#1}` and using `range@#1@lastq` as value testing the define state.
\makeatother
\begin{document}

\newcounter{fqr}
\newcounter{lqr}
\setcounter{fqr}{\firstqinrange{short}}%%use `\firstqinrange{short}` to set the counter
\setcounter{lqr}{\lastqinrange{short}}%%use `\firstqinrange{short}` to set the counter

\begin{tabular}{|c|c|c|}
    \hline
    Qns & Points & Score \\\hline
    MCQ & \pointsinrange{mcq} & \\\hline
    \whiledo{ \not \( \thefqr > \thelqr \) }{%
        \ifthenelse{
        \not \( \thefqr > \thelqr \) }{%
            \thefqr & \pointsofquestion{\thefqr} & \\ \hline%
        }{blah}%
        \stepcounter{fqr}%
    }
    Total: & \numpoints & \\\hline
\end{tabular}

\begin{questions}
    \section{MCQ Section}
    \begingradingrange{mcq}
        \question[2]
        MCQ Question 1

        \question[2]
        MCQ Question 2

        \question[2]
        MCQ Question 3
    \endgradingrange{mcq}

    \section{Short Answer Section}
    \begingradingrange{short}
        \question
        This question has 3 parts.
        \begin{parts}
            \part[2]
                Part 1.
            \part[2]
                Part 2.
            \part[3]
                Part 3
        \end{parts}

        \question
        This question has 2 parts.
            \begin{parts}
            \part[5]
                Part 1.
            \part[4]
                Part 2.
        \end{parts}
    \endgradingrange{short}
    \end{questions}

\end{document}

现在代码正在用于\setcounter{fqr}{\firstqinrange{short}}设置计数器,并且成功编译而没有错误。

相关内容