将 \newcommand 更改为 xparse \NewDocumentCommand

将 \newcommand 更改为 xparse \NewDocumentCommand

我想将 改为\newcommand \shcpointxparse \NewDocumentCommand我该如何实现?

\newcounter{totalpoints}
\regtotcounter{totalpoints} 

\newcommand{\shcpoint}[1]{%
  \addtocounter{totalpoints}{#1}%
  \ifnum#1<2
    (\emph{#1 point})\hphantom{\em s}%
  \else
    (\emph{#1 points})%
  \fi\quad
}

这是我的尝试:

\NewDocumentCommand{\shcpoint}{ m }
{%
  \addtocounter{totalpoints}{#1}%
  \ifnum#1<2
    (\emph{#1 point})\hphantom{\em s}%
  \else
    (\emph{#1 points})%
  \fi\quad
}

我的问题延伸到:在 LaTeX 3 语法中是否有与\addtocounter\ifnum和等效\else的东西?\fi

答案1

如果以代码来衡量,你实际上并没有获得太多好处。好处在于灵活性和清晰度。以下是一种可能的实现:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\shcpoint}{ m }
 {
  \azetina_shcpoint:n { #1 }
 }

\NewDocumentCommand{\printtotal}{ }
 {
  \int_to_arabic:n { \g_azetina_totalpoints_int }
 }

\int_new:N \g_azetina_totalpoints_int

\cs_new_protected:Npn \azetina_shcpoint:n #1
 {
  \int_gadd:Nn \g_azetina_totalpoints_int { #1 }
  \int_compare:nTF { #1 < 2 }
    {
     (\emph{#1 ~ point})\hphantom{\em s}
    }
    {
     (\emph{#1 ~ points})
    }
  \quad
}
\ExplSyntaxOff

\begin{document}

\shcpoint{1} X

\shcpoint{3} X

\shcpoint{1} X

Total points: \printtotal

\end{document}

在此处输入图片描述

需要注意的一些事项。

  1. 当定义的命令的\NewDocumentCommand功能超出用户级命令所能实现的功能时,它应该调用程序员级函数。

  2. 由于我们要进入编程环境,因此我们需要发出\ExplSyntaxOn

  3. 由于您的计数器totalpoints用于跟踪点而不是用于除最后之外的排版值,因此我将其声明为整数(全局)变量。

  4. 我还定义了用于打印变量值的命令;因为它只使用一个内部函数,所以没有必要定义我们自己的内部函数。

  5. 在编程环境中,空间是被忽略(除非它们在语法上对于分隔命令名称有意义),因此输出中的空格通常用 表示~

  6. 该内部函数有一个参数,因此其名称以 结尾:n

  7. \azetina_shcpoint:n是“受保护的”,因为它执行了一项任务。

  8. 我们做一个“整数比较”:

    \int_compare:nTF { <condition> }
     {
      <true case>
     }
     {
      <false case>
     }
    

David Carlisle 正确地指出,使用“point”或“points”时可能会有微小的差异。以下是代码的修改版本,它不应该有问题,并且还说明了另一点:比较也可以

\int_compare:nT { <condition> } { <true case> }

如果不需要执行任何操作,则忽略 false 情况。在这里,我们测量带有“s”的文本(假设每个问题的点数小于 10,并使用 0 作为数字的默认宽度),并将最终结果排版到正确宽度的框中。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\shcpoint}{ m }
 {
  \azetina_shcpoint:n { #1 }
 }

\NewDocumentCommand{\printtotal}{ }
 {
  \int_to_arabic:n { \g_azetina_totalpoints_int }
 }

\int_new:N \g_azetina_totalpoints_int
\box_new:N \l_azetina_points_box
\cs_new_protected:Npn \azetina_shcpoint:n #1
 {
  \int_gadd:Nn \g_azetina_totalpoints_int { #1 }
  \hbox_set:Nn \l_azetina_points_box { (0 ~ \textit{points}) }
  \makebox[\box_wd:N \l_azetina_points_box][l]
   {
    (\textit{#1 ~ point\int_compare:nT { #1 > 1 } { s }})
   }
  \quad
}
\ExplSyntaxOff

\begin{document}

\shcpoint{1} X

\shcpoint{3} X

\shcpoint{1} X

Total points: \printtotal

\end{document}

在此处输入图片描述

如果您尝试使用\showoutput,您会发现所有相关框的宽度均为 42.7429pt。

答案2

当前的 expl3 编程层确实针对的是低于这个水平,所以我认为没问题。但是代码并没有实现我猜测的将或设置为相同宽度\addtocounter的意图。此外,它应该使用(或更好的)而不是。pointpoints\textit\itshape\emph

\sbox0{\emph{points}\quad}\showthe\wd0
\sbox0{\emph{point}\hphantom{\em s}\quad}\showthe\wd0

生产

> 36.63177pt.
l.13 \sbox0{\emph{points}\quad}\showthe\wd0

? 
> 36.75957pt.
l.14 ...h{point}\hphantom{\em s}\quad}\showthe\wd0

如您所见,它们并不相等。要了解原因,请考虑

\documentclass{article}

    \begin{document}

    \showoutput

    1 \emph{points}\quad

    2 \emph{point}\hphantom{\em s}\quad


    \end{document}

产生

....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 1
....\glue 3.33333 plus 1.66666 minus 1.11111
....\OT1/cmr/m/it/10 p
....\kern-0.51112
....\OT1/cmr/m/it/10 o
....\OT1/cmr/m/it/10 i
....\OT1/cmr/m/it/10 n
....\OT1/cmr/m/it/10 t
....\OT1/cmr/m/it/10 s
....\kern 0.82082
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
....\glue(\rightskip) 0.0
...\glue(\parskip) 0.0 plus 1.0
...\glue(\baselineskip) 3.50197
...\hbox(6.55359+1.94444)x345.0, glue set 294.9071fil
....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 2
....\glue 3.33333 plus 1.66666 minus 1.11111
....\OT1/cmr/m/it/10 p
....\kern-0.51112
....\OT1/cmr/m/it/10 o
....\OT1/cmr/m/it/10 i
....\OT1/cmr/m/it/10 n
....\OT1/cmr/m/it/10 t
....\kern 0.94861
....\hbox(0.0+0.0)x4.08887
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
....\glue(\rightskip) 0.0

您可以看到差异在于,差异\kern是斜体校正(由 添加\emph)分别用于s和。如果尾随命令是而不是 ,t您会看到更大的差异,因为在第一种情况下斜体校正将被抑制,但在第二种情况下不会。.\quad

在这种情况下你可以使用

{\itshape points}\quad}

{\itshape point}\hphantom{\itshape s}\quad}

t但这依赖于在和之间的字体度量中没有指定字距s

更安全的构造是

\settowidth\dimen@{\itshape points}%
\makebox[\dimen@][l]{point}\quad

相关内容