我感觉这会非常简单,但我一直不知道如何去做。
考虑以下代码:
\documentclass{article}
\newcommand{\items}[2] {
\begin{itemize}
\item #1
\item #2
\end{itemize}
}
\begin{document}
\items{Foo}{Bar}
\end{document}
输出如下所示:
现在,假设参数 2 为空:
\documentclass{article}
\newcommand{\items}[2] {
\begin{itemize}
\item #1
\item #2
\end{itemize}
}
\begin{document}
\items{Foo}{}
\end{document}
不出所料,我们得到了这样的结果:
问题是,我怎样才能使第二项根本不出现,就像这样?
4
最佳答案
4
您可以使用,\ifstrempty
例如etoolbox
:
\documentclass{article}
\usepackage{etoolbox}
\newcommand{\items}[2] {
\begin{itemize}
\item #1
\ifstrempty{#2}{}{\item #2}%
\end{itemize}
}
\begin{document}
Foo:
\items{Foo1}{Foo2}
Bar:
\items{Bar1}{}
\end{document}
注意:\ifstrempty
不扩展参数,并且像 中的空格\items{Foo}{ }
不是空的。对于空或空格,您可以替换\ifstrempty
为\ifblank
(有关这些命令和类似命令的更多信息,请参阅文档)或较新的 LaTeX 内核命令(2022-06-01 或更新版本)\IfBlankTF
。
还有其他具有类似功能的软件包,例如scrbase
:
\documentclass{article}
\usepackage{scrbase}
\newcommand{\items}[2] {
\begin{itemize}
\item #1
\IfArgIsEmpty{#2}{}{\item #2}%
\end{itemize}
}
\begin{document}
\items{Foo1}{Bar1}
\items{Foo2}{}
\end{document}
scrbase
还提供了\Ifstr
扩展参数的函数。有关更多信息,请参阅
但是你也可以用可变数量的参数做一些棘手的事情:
\documentclass{article}
\newcommand{\items}{\begin{itemize}\processitems}
\makeatletter
\newcommand{\processitems}[1]{\item
#1\@ifnextchar\bgroup{\processitems}{\end{itemize}}}
\makeatother
\begin{document}
Foo:
\items{Foo1}{Foo2}{Foo3}
Bar:
\items{Bar1}{Bar2}
Poo:
\items{Poo}
\end{document}
但因为这实际上并不像 LaTeX,所以您还可以对第 2、第 3、……第 n 个参数使用可变数量的可选参数:
\documentclass{article}
\newcommand{\items}{\begin{itemize}\processitems}
\NewDocumentCommand{\processitems}{mo}{
\item #1
\IfValueTF{#2}{\processitems{#2}}{\end{itemize}}
}
\begin{document}
Foo:
\items{Foo1}[Foo2][Foo3]
Bar:
\items{Bar1}[Bar2]
Poo:
\items{Poo}
\end{document}
结果和上面一样。
如果只需要两个参数,那么使用一个可选的而不是强制的第二个参数也是一个常见的解决方案:
\documentclass{article}
\NewDocumentCommand{\items}{mo}{%
\begin{itemize}
\item #1
\IfValueT{#2}{\item #2}
\end{itemize}
}
\begin{document}
Foo:
\items{Foo1}[Foo2]
Bar:
\items{Bar1}
\end{document}
对于只有两个参数{…}
且第二个参数是可选的,您可以使用g
仅在(已弃用的)包中可用的参数类型:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\items}{mg}{%
\begin{itemize}
\item #1
\IfValueT{#2}{\item #2}
\end{itemize}
}
\begin{document}
Foo:
\items{Foo1}{Foo2}
Bar:
\items{Bar1}
\end{document}
结果与上面相同。
1
-
第一个就成功了。
–
|
就像 @daleif 在他的评论中提到的那样,您可以使用内核内置的检查来查看参数是否为空并采取相应措施。没有\IfEmptyTF
,但您可以改用,\IfBlankTF
如果参数仅包含空格,它也将为真。
\documentclass{article}
\newcommand{\items}[2]{%
\begin{itemize}%
\item #1%
\IfBlankF{#2}{\item #2}%
\end{itemize}%
}
\begin{document}
\items{Foo}{}
\end{document}
另一个版本以 LaTeX 友好的语法接受任意多项:
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand \items { m }
{
\begin{itemize}
\tl_map_function:nN {#1} \item
\end{itemize}
}
\ExplSyntaxOff
\begin{document}
Single item is harder: \items{{Foo}}
Multiple items is simple and very flexible: \items{{Foo}{Bar}{Baz}}
\end{document}
7
-
当我尝试使用 进行编译时
pdflatex
,失败了! Undefined control sequence. \items #1#2->\begin {itemize}\item #1\IfBlankF {#2}{\item #2}\end {itemize} l.9 \items{Foo}{}
?
– -
@KristianNordestgaard – 您使用的是哪个 TeX 发行版,上次更新是什么时候?我之所以问这个问题,是因为答案在我的 TeXLive2024 发行版(安装了所有更新)上可以顺利运行。
– -
@Mico
pdfTeX 3.141592653-2.6-1.40.23 (TeX Live 2021)
。
– -
2@KristianNordestgaard 对于
\IfBlankTF
,,您至少需要 LaTeX 2022-06-01。对于较旧的 LaTeX 内核,您可以使用\IfBlankT
→我的答案。\IfBlankF
etoolbox
– -
2@KristianNordestgaard 你可以
\ExplSyntaxOn\cs_new_eq:NN \IfBlankF \tl_if_blank:nF\ExplSyntaxOff
在 TL 2021 的序言中使用它,答案应该有效。
–
|
简单的:
\newcommand{\items}[2]{%
\begin{itemize}
\item #1 \if\relax\detokenize{#2}\relax\else\item #2\fi
\end{itemize}
}
接下来,如何对三个或更多项目执行相同操作?如果超出了九个允许的参数,该怎么办?
让我们更笼统一点。你必须用参数列表做一些事情,这些参数可以是从零到任意数字。例如,建立一个逐项列表(或枚举列表,或gather
一堆公式)。
\documentclass{article}
\usepackage{amsmath}
\ExplSyntaxOn
\NewDocumentCommand{\processarguments}{mmmO{}m}
{% #1 = pre, #2 = post, #3 = template, #4 = separator, #5 = list of items
\nordestgaard_processargs:nnnnn {#1} {#2} {#3} {#4} {#5}
}
\seq_new:N \l__nordestgaard_processargs_items_seq
\cs_new_protected:Nn \__nordestgaard_processargs_do:n {}
\cs_new_protected:Nn \nordestgaard_processargs:nnnnn
{
% populate the sequence
\seq_clear:N \l__nordestgaard_processargs_items_seq
\tl_map_inline:nn {#5}
{
\seq_put_right:Nn \l__nordestgaard_processargs_items_seq
{% we want to apply the template on the items
\__nordestgaard_processargs_do:n {##1}
}
}
% define the processing command to the template
\cs_set_protected:Nn \__nordestgaard_processargs_do:n {#3}
% issue the 'pre' tokens
#1
% use the sequence
\seq_use:Nn \l__nordestgaard_processargs_items_seq {#4}
% issue the 'post' tokens
#2
}
\ExplSyntaxOff
\begin{document}
One item
\processarguments{\begin{itemize}}{\end{itemize}}{\item #1}{
{Foo}
}
Two items
\processarguments{\begin{itemize}}{\end{itemize}}{\item #1}{
{Foo}
{Bar}
}
Three items
\processarguments{\begin{enumerate}}{\end{enumerate}}{\item #1}{
{Foo}
{Bar}
{Again!}
}
We can gather formulas
\processarguments{\begin{gather}}{\end{gather}}{#1}[\\]{
{(a+b)+c=a+(b+c)}
{x+y=y+x}
{xy=yx}
}
\end{document}
当然,您可以在此基础上进行构建,并将您的\items
命令定义为
\newcommand{\items}[1]{%
\processarguments{\begin{itemize}}{\end{itemize}}{\item ##1}{#1}%
}
请注意,##1
因为我们在定义里面。
或者更好的是,请参阅下面的代码。
\documentclass{article}
\usepackage{amsmath}
\ExplSyntaxOn
\NewDocumentCommand{\processarguments}{mmmO{}m}
{% #1 = pre, #2 = post, #3 = template, #4 = separator, #5 = list of items
\nordestgaard_processargs:nnnnn {#1} {#2} {#3} {#4} {#5}
}
\seq_new:N \l__nordestgaard_processargs_items_seq
\cs_new_protected:Nn \__nordestgaard_processargs_do:n {}
\cs_new_protected:Nn \nordestgaard_processargs:nnnnn
{
% populate the sequence
\seq_clear:N \l__nordestgaard_processargs_items_seq
\tl_map_inline:nn {#5}
{
\seq_put_right:Nn \l__nordestgaard_processargs_items_seq
{% we want to apply the template on the items
\__nordestgaard_processargs_do:n {##1}
}
}
% define the processing command to the template
\cs_set_protected:Nn \__nordestgaard_processargs_do:n {#3}
% issue the 'pre' tokens
#1
% use the sequence
\seq_use:Nn \l__nordestgaard_processargs_items_seq {#4}
% issue the 'post' tokens
#2
}
\ExplSyntaxOff
%%% now we define a few instances
% first your \items
\NewDocumentCommand{\items}{m}{%
\processarguments{\begin{itemize}}{\end{itemize}}{\item ##1}{#1}%
}
% next a better one
\NewDocumentCommand{\xitems}{O{itemize}m}{%
\processarguments{\begin{#1}}{\end{#1}}{\item ##1}{#2}%
}
\begin{document}
One item
\items{
{Foo}
}
Two items
\items{
{Foo}
{Bar}
}
Three items
\xitems[enumerate]{
{Foo}
{Bar}
{Again!}
}
\end{document}
使用此语法,输入只需要多两个括号,但可以容纳任意数量的项目。
|
在 TeX 原始级别测试参数是否为空的传统方法是,\ifx ?#1?
并且您必须假设?
永远不会作为的第一个标记出现#1
。如果#1
为空,则测试简化为\ifx ??
,这是正确的,因为“?”等于“?”。如果#1
不为空(例如#1
为abc
),则测试看起来像\ifx ?abc?
,并且因为?
不等于,所以a
跳过以下文本,直到\else
达到\fi
。
在您的示例中,项目文本永远不会以 开头,^
因为此标记仅在数学模式下使用。因此,您的测试应该是:
\documentclass{article}
\newcommand{\items}[2] {
\begin{itemize}
\item #1
\ifx ^#2^\else \item #2\fi
\end{itemize}
}
\begin{document}
\items{Foo}{}
\end{document}
如果您想要实现的通用可扩展测试为#1
空,请使用\if^\detokenize{#1}^
。\if
原语会扩展\detokenize
并将所有标记转换为 catcode 12(或空格为 10),因此 catcode 为 7 的标记^
永远不会是\detokenize
输出的第一个标记。
0
|
–
–
–
\IfNoValueTF
xparser 包,尽管它仅适用于可选参数。–
|