如何编写具有可变数量文本变量的宏

如何编写具有可变数量文本变量的宏

我正在尝试制作一个接受可变数量文本变量的签名块宏,这样我就可以添加或减去文本而不使用空字段“{}”作为参数。

\sigblock{0}{2}{Notary Public} 生成:

在此处输入图片描述

和,

\sigblock{0}{3}{Notary Public}{At Large}

生成:

在此处输入图片描述

在宏中,前两个变量是线的水平位置和线的长度,其余变量是文本。

我尝试过这个:

 \documentclass{article}
 \usepackage{ifthen}

\def\sigblock#1#2#3#4% Defined 4 arguments%
   {\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
    \par\noindent\hskip#1in#3\par\noindent\hskip#1in#4}}

\def\sigblockB#1#2#3% Defined 3 arguments%
{\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
    \par\noindent\hskip#1in#3}}

\ifthenelse{\equal{%
        \sigblock}{#1}{#2}{#3}}%
{\renewcommand{\sigblockB}{%
        \sigblock%
                    }}

\begin{document}

\sigblock{0}{3}{Notary Public}{At Large}

\sigblock{0}{3}{Notary Public}

\sigblockB{0}{3}{Notary Public}

\end{document}

使用 lualatex 编译时生成:

在此处输入图片描述

日志文件中还有大量错误。在我的 MWE 中,我尝试使用替换\renewcommand。我确实想到可能有一个涉及 的解决方案。一个可以“扩展”以允许更多文本行的解决方案是最好的。\sigblock\sigblockB\let

当我填写签名栏时,我经常需要剪切和粘贴信息。我个人认为,一开始使用一系列括号更容易做到这一点,之后对校对也很有帮助。

我意识到我的问题反映了我对变量工作原理的无知。如果能解释一下我的尝试从根本上出了什么问题,并给出解决方案,我将不胜感激。

有关的:创建右对齐签名块

答案1

虽然我建议使用@egreg的答案,但这里有一种方法可以实现你正在寻找的使用\@ifnextchar。这个想法是\sigblock准备好一切,直到处理签名字段下方的行。然后\sigblock@调用它将下一个分组参数设置为签名字段下方的一行并开始递归。当没有剩余参数时,递归终止:

\def\sigblock@#1#2{%
  \par\hskip#1in#2
  \@ifnextchar\bgroup{\sigblock@{#1}}\endgroup
}

输出1_cut 输出2_cut 输出3_cut

\documentclass{article}
\usepackage{lipsum}
\usepackage[doublespacing]{setspace}%doublespacing is active for testing purposes

\makeatletter
\def\sigblock#1#2#3{%
  \begingroup
  \singlespacing
  \parindent\z@
  \vskip.75in
  \vbox{%
    \hskip#1in%
    \hbox to #2in{%
      \leaders\hbox to 0.00625in{\hfil.\hfil}\hfill
    }
  }%
  \sigblock@{#1}{#3}
}
\def\sigblock@#1#2{%
  \par\hskip#1in#2\par
  \@ifnextchar\bgroup{\sigblock@{#1}}\endgroup
}
\makeatother


\begin{document}
\sigblock{0}{3}{Notary Public}{At Large}{At Large}{At Large}{At Large}{At Large}
\lipsum
\end{document}

笔记。由于额外的行是可选的,您可能希望\sigblock{0}{3}{Notary Public}[At Large]...遵循标准接口设计,用括号表示可选参数。在这种情况下,您可以定义:

\def\sigblock#1#2#3{%
  \begingroup
  \singlespacing
  \parindent\z@
  \vskip.75in
  \vbox{%
    \hskip#1in%
    \hbox to #2in{%
      \leaders\hbox to 0.00625in{\hfil.\hfil}\hfill
    }
  }%
  \sigblock@{#1}[#3]
}
\def\sigblock@#1[#2]{%
  \par\hskip#1in#2\par
  \@ifnextchar[{\sigblock@{#1}}\endgroup
}

答案2

您想太多了:工具已经在那里了,即tabular

\documentclass{article}

\newcommand{\sigblock}[3]{%
  \par\vspace{\medskipamount}\noindent
  \hspace*{#1in}\makebox[#2in]{\hrulefill}\\*[.2ex]
  \hspace*{#1in}%
  \begin{tabular}{@{}l@{}}
  #3
  \end{tabular}%
}

\begin{document}

\sigblock{0}{3}{Notary Public \\ At Large}

\sigblock{0}{3}{Notary Public}

\sigblock{1}{3}{Notary Public \\ At Large \\ Again \\ What else?}

\end{document}

在此处输入图片描述

如果你需要添加\singlespacing,请记住不是带有参数的命令,但带有声明。因此你应该

\newcommand{\sigblock}[3]{%
  \par{\singlespacing\vspace{\medskipamount}\noindent
  \hspace*{#1in}\makebox[#2in]{\hrulefill}\\*[.2ex]
  \hspace*{#1in}%
  \begin{tabular}{@{}l@{}}
  #3
  \end{tabular}%
  \par}%
}

答案3

在尝试解决这个问题的过程中,我想到了一个可能很有趣的宏,所以我想在这里分享它。这个宏被调用\vardef,并允许我们定义一个宏,它可以接收用大括号括起来的可变数量的参数(任意数量,不限于 9 个参数)。

在内联宏的定义中,\vardef可以使用宏\NUMARGS来检索宏在“执行”时接收的参数数量;并且可以使用宏、、、\ARG1...来检索宏的参数(如果存在)。\ARG2\ARG3

例如,如果将\vardef宏定义\mymacro如下,

\newcount\tmpnum
\vardef\mymacro{This macro is being used with \NUMARGS{} arguments.
  They are: \ifnum\NUMARGS>0 \tmpnum=1 \loop \ARG{\the\tmpnum}
  \ifnum\NUMARGS>\tmpnum \advance\tmpnum by 1 \repeat\fi}

然后“执行”\mymacro{first}{second}{third}给出“输出”

 This macro is being used with 3 arguments. They are: first second third

\mymacro{first}{second}{third}{fourth}{fifth}并给予“执行”

This macro is being used with 5 arguments. They are: first second third fourth fifth

\vardef定义方式如下:

% Firstly we create a new counter to store the number 
% of the current argument that is being read. 
% Arguments start from 1, so we initially set it to 1.
\newcount\vardefnum
\vardefnum=1

% \vardef receives two arguments: the first is the name of
% the macro to be \vardef-ined and the second is its definition.
\def\vardef#1#2{%
  % We momentaneously store the definition in a macro called
  % \vardefinition for later use.
  \def\vardefinition{#2}
  % Now we actually define our control sequence.  We define it
  % basically to be \grab (a macro whose purpose is explained below).
  \def#1{%
    % Before actually grabbing arguments with \grab, we give
    % an empty useless definition to \invardef so  that we will be able
    % later to know that we are in the defining code  of a
    % \vardef-ined macro (we will see why later).
    \def\invardef{}%
    \grab}}

% The purpose of \grab is to collect and store all the arguments
% that follow him and to finally actually "execute" the code we
% previously stored in \vardefinition.
% To do that, \grab starts by saving into \next the first token
% after him. Then he passes the job to his sister \grabA.
\def\grab{\futurelet\next\grabA}

% \grabA checks if the token stored in \next is an opening curly
% brackets (or, more properly, a \bgroup). If so, she invokes
% the macro \storeargs. Otherwise she defines the number of
% arguments \numvarargs collected by now to be one less than
% \vardefnum;  then she expands \vardefinition and removes
% the traces of her job by calling \clearvardef.
\def\grabA{\ifx\next\bgroup\storearg\else\advance\vardefnum by-1
  \edef\numvarargs{\the\vardefnum}\vardefinition\clearvardef\fi}

% \clearvardef undefines \invardef (because now we are exiting
% the argument of the \vardef-ined macro) and restores the
% \vardefnum counter.
\def\clearvardef{\let\invardef\undefined \vardefnum=1\relax}

% \storearg, when is invoked by \grabA, finds on its way the
% sequence \else\advance\vardefnum ... \clearvardef\fi. He wants
% to get rid of it to be able to see what's behind it:  so he
% eats all of it using his first argument (the argument #1).
% Now \storearg sees after him an argument enclosed in curly
% brackets. So he puts it in his second argument (the argument #2).
% Next, to make TeX happy, \storearg has to regurgitate
% the \fi he has previously eaten up. After that he defines a new
% control sequence that stores his second argument he has just
% collected. The name of this control sequence starts with
% 'vararg' and ends with the number present in \vardefnum at that
% moment. Finally \storearg increments \vardefnum and takes a rest
% by giving the job back to \grab.
\def\storearg#1\fi#2{\fi
  \expandafter\def\csname vararg\the\vardefnum\endcsname{#2}%
  \advance\vardefnum by 1\grab}


% The following two macros, \ARG and \NUMARGS, are the user
% interface for actually using inside the \vardef-inition the
% (variable number of) arguments.  Ckecking if \invardef is
% defined, they can know if they are called inside the
% \vardef-inition of a macro, and they can throw an error message
% otherwise. What they do is quite self explanatory.
\def\ARG#1{\ifdefined\invardef\else\errmessage{%
  \string\ARG\space has meaning only inside a
  \string\vardef-ined macro.}\fi
  \ifnum#1<1\errmessage{The argument of \string\ARG\space
  must be greater than zero!}\fi
  \ifnum#1>\numvarargs\errmessage{\string\ARG#1 does not exist!}\fi
  \csname vararg#1\endcsname}

\def\NUMARGS{\ifdefined\invardef\numvarargs\else\errmessage{%
  \string\NUMARGS\space has meaning only inside a
  \string\vardef-ined macro.}\fi}

那么,所提问题的解决方案非常简单:使用\ARG1、、、... 而不是、、、... 并使用来检查已传递给了多少个参数。\ARG2\ARG3#1#2#3\NUMARGS\sigblock

\documentclass{article}
\usepackage{setspace}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% here goes the above definition of \vardef %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\vardef\sigblock{\singlespacing
  \vbox{\vskip.75in\noindent\hskip\ARG1in
  {\hbox to \ARG2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}\endgraf
  \noindent\hskip\ARG1in\ARG3
  \ifnum\NUMARGS=4\endgraf\noindent\hskip\ARG1 in\ARG4\fi}}

\begin{document}

\sigblock{0}{3}{Notary Public}{At Large}

\sigblock{0}{3}{Notary Public}

\end{document}

在此处输入图片描述

现在可以非常轻松地使用任意数量的参数\sigblock

注意:与普通宏不同,在\vardefined 宏中,所有参数都必须括在花括号中,并且参数的右括号和下一个参数的左括号之间不能有空格。

答案4

用一个\Longstack

\documentclass{article}
\documentclass{article}
\usepackage{stackengine,lipsum,setspace}
\setstackEOL{\cr}
\def\sigblock#1#2#3% Defined 3 arguments%
{\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
    \par\noindent\hskip#1in\Longstack[l]{#3}}}
\parindent 0pt
\begin{document}
\lipsum[1]

\sigblock{1}{2}{Notary Public\cr at Large}


\sigblock{0}{2.5}{Notary Public\cr at Large\cr State of Disorder}
\end{document}

在此处输入图片描述

相关内容