我正在清理一些描述 API 的旧 LaTeX 文档。在此过程中,我创建了自己的 API 描述风格,但我遇到了一些似乎迫使我违反 DRY 的事情。
为了遵循旧文档的风格,每个功能应按照以下格式描述:
<function signature>
<table describing each argument>
<description of return value>
理想情况下,我想创建一个环境和几个命令来描述参数和返回类型。比如
\begin{function}{getQuestion}
\return{string}{String with the question.}
\argument{bar}{int}{The answer to the ultimate question.}
\argument{baz}{int}{Number of years you are willing to wait for the question.}
\end{function}
这应该会产生类似
\textit{string} getQuestion ( \textit{int} bar, \textit{int} baz )
\begin{tabular}{lll}
bar & int & The answer to the ultimate question \\
baz & int & Number of years you are willing to wait for the question.
\textbf{Returns:} \textit{string} - String with the question.
但为了使其工作,我认为我需要以\argument
某种方式收集所有参数(),以便我可以对它们进行两次迭代。
过去,我只需要收集一我已经使用了\newcommand
和的值\renewcommand
,但我不知道如何使用它来收集多个值。
关于我该如何处理这个问题,有什么指点吗?
答案1
嗯,说来话长。
收集一些标记然后处理它们的最常见方法是将它们存储在所谓的标记寄存器中,或者将新标记添加到现有命令中。第一个技巧是将它们添加到现有标记寄存器或命令中,而不是覆盖现有值。您可以使用低级 TeX 原语来执行此操作\expandafter
。对于命令,这看起来像
\expandafter\def\expandafter\cmd\expandafter{\cmd <new contents here>}
告诉\expandafter
TeX 暂时忽略下一个命令(或标记),并处理其后的命令。(准确地说是一级扩展。)例如,
\def\cmd{A}
\expandafter\def\expandafter\cmd\expandafter{\cmd <new contents here>}
有效地
\def\cmd{A<new contents here>}
令牌寄存器的工作方式略有不同,但其思想是一样的:
\newtoks{\argumenttoks} % you must allocate a toks once, ever, before using it
...
\argumenttoks={A}% like \(re)newcommand\cmd{A}
\argumenttoks=\expandafter{\the\argumenttoks <new contents here>}
这两种方法之间的区别在于 1)速度(toks 更快)和 2)toks 允许使用像 # 这样的 TeX 字符,并且\def
或\(re)newcommand
不喜欢那样,以及 3)toks 被认为是相当低级的,并且它们的数量是有限的,你必须(通过\newtoks
)声明。
现在我们已经完成了一半。接下来的问题是:我如何读取列表?奇怪的是,读取列表的最常见方法是执行它。但是要使用命令处理每个项目,您需要能够在每个列表元素之前添加该命令。常用的技巧是在构建列表时实际添加该命令:
\newcommand{\argument}[3]{%
\argumenttoks=\expandafter{\the\argumenttoks \showarg{#1}{#2}{#3}}%
}
这边走,
\argument{bar}{int}{The answer to the ultimate question.}
\argument{baz}{int}{Number of years you are willing to wait for the question.}
存储为列表
\showarg{bar}{int}{The answer to the ultimate question.}
\showarg{baz}{int}{Number of years you are willing to wait for the question.}
因此,要使用列表,您必须在正确的位置执行它,并确保\showarg
在该点执行正确的事情。
\newtoks{\argumenttoks}
\newenvironment{function}[1]{%
\argumenttoks={}% make sure it's empty
% other stuff here
}{% \end{function}
\textit{\storedreturntype} \storedfunctionname
% now the arglist
\let\showarg=\showargcommaseparated
\let\argcomma=\empty % no comma at the start
(\the\argumenttoks)
...
% now the table
\let\showarg=\showargtabular
\let\argrowsep=\empty
\begin{tabular}{lll}\the\argumenttoks\end{tabular}
...
}
\newcommand{\showargcommaseparated}[3]{%
\argcomma % first time empty, second time a comma
\def\argcomma{, }% next time
\textit{#2} #1%
}
\newcommand{\showargtabular}[3]{%
\argrowsep % first time empty, second time a \\
#1 & #2 & #3
\def\argrowsep{\\}% next time
}
如果您不熟悉低级 TeX:\def
就像\newcommand
那样从不抱怨(无论命令是否存在它都会工作,因此它非常适合覆盖您自己的命令),并将\let
一个命令的定义复制到另一个命令,这样您就可以快速切换命令的含义,就像我对命令所做的那样\showarg
。
请注意,低级 LaTeX 程序员会进一步优化这一点,但使用自修改命令等可能会有点令人难以置信,我认为这已经变得足够困难了!
答案2
我的 LaTeX 已经生疏了,但也许这个有帮助(摘自http://web.mit.edu/annakot/MacData/afs/sipb/project/tex/doc/latex/base/usrguide/node18.html):
\newcommand{\example}[2][YYY]{Mandatory arg: #2; Optional arg: #1.}
这定义
\example
为一个带有两个参数的命令,在--到目前为止没有什么新#1
东西。但是通过向这个( )添加第二个可选参数,新定义的命令的 第一个参数 ( )变为可选,其默认值为。#2
{<definition>}
\newcommand
[YYY]
#1
\example
YYY