我正在尝试排版一个类似 OCaml 的列表,并想mylist
为其定义一个命令。预期结果如下所示:
\mylist{} --> []
\mylist{aa} --> [aa]
\mylist{aa,bb} --> [aa;bb]
\mylist{aa,bb,cc} --> [aa;bb;cc]
我希望它,
始终是类型化的分隔符(在 LaTeX 中),但在 pdf 文档中显示为另一个符号(这里是;
)(在定义中对显示的符号进行硬编码是可以接受的mylist
)。
搜索了一段时间后我发现这个答案并做了一些改变。
\newcounter{listcount}\newcounter{totalcount}%
\newcommand{\mylist}[1]{%
\setcounter{totalcount}{0}% Reset total count
\renewcommand*{\do}[1]{\stepcounter{totalcount}}% Reconfigure count
\docsvlist{#1}% Count number of items
\setcounter{listcount}{0}% Reset current item count
\renewcommand*{\do}[1]{% Reconfigure item \do
\stepcounter{listcount}% Next item
##1;\ifnum\value{listcount}<\value{totalcount}\,\fi% Print item
}
\left[\docsvlist{#1}\right]% Process list
}
问题是,当列表包含 >1 个元素时,会出现尾随;
。
\mylist{aa,bb} --> [aa;bb;]
直观上,需要测试元素是否是最后一个。但是我似乎对宏定义listcount
和逗号的数量感到困惑。
我还发现使用forcsvlist
但问题是相似的。似乎一个办法通过使用其他包,但有点过度,所以我倾向于不使用它。
答案1
;
必须移动角色才能将其放置在\ifnum\value....\fi
测试内部。
我还添加了非常非常多expl3
之前的较短版本\begin{document}
,尽管expl3
语法对于 L3 新手来说可能非常奇怪......
我两年前(2015 年)一直使用\docsvlist
和宏,但后来转向了功能更强大的语法(虽然不容易学习),尤其是对于中的大量列表,例如或或列表,在我看来,它们提供了更多功能。 \forcsvlist
expl3
expl3
seq
clist
prop
但首先要进行\ifnum...
测试:
\ifnum\value{listcount} < \value{totalcount}
;\,
\fi
只是测试计数器的当前值是否listcount
小于totalcount
计数器的值并打印;\,
,否则不打印。
该expl3
版本位于最后,使用起来更加简单,不需要测试(事实上,它们隐藏在后面\seq_use
)。
首先使用expl3
- 语法样式并将\ExplSyntaxOn
参数的逗号分隔列表值存储到临时序列变量中\l_tmpa_seq
。稍后使用\seq_use:Nn \l_tmpa_seq {;\,}
- 显示此列表,该命令会处理列表的末尾。
为了防止空间占用,包装器命令\displaylist
定义在外面,另请参阅
l3prop 中的空格有关带有 的空间的主题expl3
。
\documentclass{article}
\usepackage{etoolbox}
\newcounter{listcount}
\newcounter{totalcount}%
\newcommand{\mylist}[1]{%
\setcounter{totalcount}{0}% Reset total count
\renewcommand*{\do}[1]{\stepcounter{totalcount}}% Reconfigure count
\docsvlist{#1}% Count number of items
\setcounter{listcount}{0}% Reset current item count
\renewcommand*{\do}[1]{% Reconfigure item \do
\stepcounter{listcount}% Next item
##1\ifnum\value{listcount}<\value{totalcount};\,\fi% Print item
}
\left[\docsvlist{#1}\right]% Process list
}
\begin{document}
$\mylist{aa,bb,cc}$
$\mylist{aa}$
\end{document}
当然$\mylist{}$
会屈服[]
。
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\newcommand{\myotherlist}[1]{%
\seq_set_from_clist:Nn \l_tmpa_seq {#1}
\displaylist{\seq_use:Nn \l_tmpa_seq {;\,}}
}
\ExplSyntaxOff
\newcommand{\displaylist}[1]{%
\left[#1\right]%
}
\begin{document}
$\myotherlist{}$
$\myotherlist{aa,bb,cc}$
$\myotherlist{aa}$
\end{document}
答案2
以下是一个替代解决方案,它不需要迭代列表两次。在第一个元素之后,\do
被替换为\mydo
:
\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\mydo}[1]{;\,#1}
\newcommand{\mylist}[1]{%
\renewcommand*{\do}[1]{##1\let\do\mydo}%
\left[\docsvlist{#1}\right]% Process list
}
\begin{document}
$\mylist{aa,bb,cc}$
$\mylist{aa}$
\end{document}
答案3
完全可扩展的版本:
\documentclass{article}
\makeatletter
\newcommand{\ocamllist}[1]{\left[\ocaml@list#1,,\@nil}
\def\ocaml@list#1,#2,#3\@nil{%
\if\relax\detokenize{#2}\relax
\expandafter\@firstoftwo % no comma
\else
\expandafter\@secondoftwo
\fi
{\if\relax\detokenize{#1}\relax\,\else#1\fi\right]}
{#1;\ocaml@list#2,#3,\@nil}
}
\makeatother
\begin{document}
$\ocamllist{}$
$\ocamllist{aa}$
$\ocamllist{aa,bb}$
$\ocamllist{aa,bb,cc}$
\end{document}
不可扩展,但更紧凑:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\ocamllist}{m}
{
\left[
\clist_set:Nn \l_hongxu_ocamllist_clist {#1}
\clist_if_empty:NTF \l_hongxu_ocamllist_clist
{ \, }
{ \clist_use:Nn \l_hongxu_ocamllist_clist {;} }
\right]
}
\clist_new:N \l_hongxu_ocamllist_clist
\ExplSyntaxOff
\begin{document}
$\ocamllist{}$
$\ocamllist{aa}$
$\ocamllist{aa,bb}$
$\ocamllist{aa,bb,cc}$
\end{document}
答案4
这是一个仅使用 TeX 原语的可扩展解决方案:
\def\ocamlist#1{\ocamlistA #1,,}
\def\ocamlistA#1,{[#1\ocamlistB}
\def\ocamlistB#1,{\ifx,#1,#1]\else;#1\expandafter\ocamlistB\fi}
$\ocamlist{aa,bb}$
\end