参数失控?使用 \newcommand

参数失控?使用 \newcommand

我有一个日期(格式为 YYYY.MM.DD)和一个时间(格式为 HH:MM)。

根据这些,我想计算一种 时间戳/总分钟数。

\documentclass{article}

\makeatletter

% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
    \expandafter\timeStamp@t#1 #2\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
    \the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

\makeatother

\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}\\%
\end{document}

编译器说:“失控参数?”

这可能是由于第二个参数的扩展存在问题,因为这个调用(\timeStamp{\dOne}{03:00})是可以的。我做错了什么?

答案1

\expandafter仅在后面的标记后扩展一个标记(除非有参数)。 在你的情况下,这只是。 获取结果的一种方法是,在插入之前在宏中#1收集扩展:#1 #2

示例输出

\documentclass{article}

\makeatletter

% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
  \edef\mytmp{#1 #2}\expandafter\timeStamp@t\mytmp\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
    \the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

\makeatother

\begin{document}
\tracingmacros=2\tracingcommands=2
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}
\end{document}

这种方法可以很好地推广到两个以上的论点。

答案2

人们可以对 进行一些处理\expandafter,但直接的方法可能更好:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new:Nn \joseph_time_stamp:nn
 {
  \__joseph_time_stamp:w #1.#2\q_stop
 }
\cs_generate_variant:Nn \joseph_time_stamp:nn { ff }
% a devious trick for the colon
\use:x
 {
  \cs_new:Npn
   \exp_not:N \__joseph_time_stamp:w
   ##1.##2.##3.##4\token_to_str:N :##5
   \exp_not:N \q_stop
 }
 {
  \int_eval:n
   {
    #5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
   }
 }
\NewExpandableDocumentCommand{\timeStamp}{mm}
 {
  \joseph_time_stamp:ff { #1 } { #2 }
 }
\ExplSyntaxOff

\begin{document}

\def\tOne{03:00}
\def\dOne{2017.08.01}

\timeStamp{\dOne}{\tOne}

\timeStamp{2017.1.1}{0:0} % should print 0

\end{document}

如果时间规范不包含冒号,那就更容易了;问题是:在的范围内是特殊的\ExplSyntaxOn,所以直接

\cs_new:Npn \__joseph_time_stamp:w #1.#2.#3.#4:#5\q_stop
 {
  \int_eval:n
   {
    #5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
   }
 }

不会起作用,需要采用间接方法来“字符串化”冒号。

在此处输入图片描述

一个可扩展的解决方案,带有\expandafter参数处理(OS针对“旧式”):

\makeatletter
\newcommand{\timeStampOS}[2]{%
  \expandafter\timeStampOS@a\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@a}[2]{%
  \expandafter\timeStampOS@b\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@b}[2]{\timeStampOS@c #1 #2\@nil}
\def\timeStampOS@c #1.#2.#3 #4:#5\@nil{%
  \the\numexpr
    #5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
  \relax
}
\makeatother

答案3

展开 #1#2

% Timestamp
\newcommand\timeStamp[2]{%
    \expandafter\timeStamp@t#2 #1\@nil}%
\def\timeStamp@t#1:#2 #3\@nil{\expandafter\timeStamp@@t#3 #1:#2\@nil}%
\def\timeStamp@@t#1.#2.#3 #4:#5\@nil{%
  \the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

答案4

更清楚的是:

  • 将宏用于子计算,可能在其他地方重复使用,

  • 在宏名中指明其对其参数的扩展类型。

像这样:(O意思是“扩展一次参数”;通常人们更喜欢使用“F”来进行 f 型扩展,它会重复扩展第一个标记,因为这允许嵌套。否则,人们将需要例如T“扩展两次参数”,因为使用\def\foo{\the\numexpr...},需要两次扩展才能完全扩展)。

在此处输入图片描述

\documentclass{article}

\newcommand{\TimeStampOO}[2]
   {\the\numexpr\expandafter\DateToMinutes\expandafter{#1}+
                \expandafter\TimeToMinutes\expandafter{#2}\relax}

\makeatletter
\newcommand{\DateToMinutes}[1]{\the\numexpr\Date@ToMinutes#1\relax}
\def\Date@ToMinutes#1.#2.#3\relax
   {(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax}
\newcommand{\TimeToMinutes}[1]{\the\numexpr\Time@ToMinutes#1\relax}
\def\Time@ToMinutes#1:#2\relax
   {#2+#1*60\relax}
\makeatother

\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\TimeStampOO{\dOne}{\tOne}%
\end{document}

这是可扩展的。

相关内容