我想将 改为\newcommand
\shcpoint
。xparse
\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}
需要注意的一些事项。
当定义的命令的
\NewDocumentCommand
功能超出用户级命令所能实现的功能时,它应该调用程序员级函数。由于我们要进入编程环境,因此我们需要发出
\ExplSyntaxOn
。由于您的计数器
totalpoints
用于跟踪点而不是用于除最后之外的排版值,因此我将其声明为整数(全局)变量。我还定义了用于打印变量值的命令;因为它只使用一个内部函数,所以没有必要定义我们自己的内部函数。
在编程环境中,空间是被忽略(除非它们在语法上对于分隔命令名称有意义),因此输出中的空格通常用 表示
~
。该内部函数有一个参数,因此其名称以 结尾
:n
。\azetina_shcpoint:n
是“受保护的”,因为它执行了一项任务。我们做一个“整数比较”:
\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
的意图。此外,它应该使用(或更好的)而不是。point
points
\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