我正在尝试创建一个带有键值参数的命令,其中第二个参数是用逗号分隔的项目列表。
当我将列表直接传递给命令时,该列表可以正常打印\DrawList
,该命令具有\SplitList
处理器和\ProcessList
命令。但是,如果在拆分之前对文本进行解析\keys_define
,它似乎会忽略逗号,处理器根本无法拆分它。
我怎样才能让它像\DrawList
命令一样打印?如果有人有更好的替代方案来简化界面,那就太好了。谢谢大家。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\usepackage{enumitem}
\NewDocumentCommand\DrawList{>{\SplitList{,}}m}{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\ProcessList{#1}{\item}
\end{itemize}
}
\keys_define:nn {module_name}{
,1 .tl_set:N = \__title
,2 .tl_set:N = \__list
}
\NewDocumentCommand \TitleAndList {O{}}{
\group_begin:
\keys_set:nn {module_name}{#1}
\tl_if_empty:NF {\__title}{%
\__title :\par
}
\tl_if_empty:NF {\__list}{%
\DrawList{\__list}
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\DrawList{{First item, with a comma}, {Second item}}
---
\TitleAndList[
1=Include,
2={{First item, with a comma}, {Second item}}
]
---
\TitleAndList[1=Not a list]
---
\TitleAndList[2=An item]
\end{document}
结果:
低层代码没有成功:
\seq_new:N \list_sequence
\NewDocumentCommand\DrawList{m}{
\seq_set_split:Nnn \list_sequence{,}{#1}
\begin{itemize}
\seq_map_function:NN \list_sequence \item
\end{itemize}
}
答案1
由于唯一一次使用\DrawList
包含列表的变量进行调用是在您自己的代码内部,因此您可以简单地使用\exp_args:NV
或\exp_args:No
(如果您知道该变量是一个标记列表,则后者就可以了)。
如果您还希望用户界面宏能够对存储列表的宏进行操作,那么您可以定义自己的处理器。
下面的操作同时执行,改变您的\TitleAndList
以扩展您转发的变量\DrawList
并定义一个处理器并将其用于\otherDrawList
。
请注意,您的变量应遵循 的命名约定expl3
,即局部变量(如您的变量)应以 开头\l
,内部变量应有两个下划线,后跟模块名称、变量的简短描述,并以缩写的变量类型结尾。因此,以下是正确的名称:(\l__thanhph_title_tl
模块的局部变量thanhph
存储名为 的内容title
,它是一个标记列表)。
\documentclass{article}
\usepackage{enumitem}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \thanhphSplitList #1 #2
{
\tl_if_single_token:nTF {#2}
{
\tl_if_head_is_space:nTF {#2}
{ \SplitList {#1} {#2} }
{
\token_if_expandable:NTF #2
{ \exp_args:Nno \SplitList {#1} {#2} }
{ \SplitList {#1} {#2} }
}
}
{ \SplitList {#1} {#2} }
}
\NewDocumentCommand \otherDrawList { >{\thanhphSplitList{,}}m }
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\ProcessList{#1}{\item}
\end{itemize}
}
\NewDocumentCommand \DrawList { >{\SplitList{,}}m }
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\ProcessList{#1}{\item}
\end{itemize}
}
\keys_define:nn { thanhph }
{
,1 .tl_set:N = \l__thanhph_title_tl
,2 .tl_set:N = \l__thanhph_list_tl
}
\NewDocumentCommand \TitleAndList {O{}}
{
\group_begin:
\keys_set:nn { thanhph } {#1}
\tl_if_empty:NF \l__thanhph_title_tl
{ \l__thanhph_title_tl :\par }
\tl_if_empty:NF \l__thanhph_list_tl
{ \exp_args:No \DrawList \l__thanhph_list_tl }
\group_end:
}
\ExplSyntaxOff
\newcommand\mylist{one item, another item}
\begin{document}
\otherDrawList\mylist
---
\DrawList{{First item, with a comma}, {Second item}}
---
\TitleAndList[
1=Include,
2={{First item, with a comma}, {Second item}}
]
---
\TitleAndList[1=Not a list]
---
\TitleAndList[2={An item, another}]
\end{document}
另一种解决方案是,如果您想扩展强制参数的第一个标记一次,则可以使用带星号的形式。我们可以相当巧妙地创建它,因为ltcmd
/xparse
允许引用处理器内的其他参数,因此我们可以将其用作\IfBooleanT{#1}\exp_args:Nno\SplitList{,}
处理器:
\NewDocumentCommand \DrawList
{ s >{ \IfBooleanT {#1} \exp_args:Nno \SplitList {,} } m }
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\ProcessList{#2}{\item}
\end{itemize}
}
然后使用
\DrawList*\mylist
获得与 相同的结果\otherDrawList\mylist
。
如果您根本不需要\DrawList
而只打算用作\TitleAndUse
接口,则可以直接映射到clist
(前提是您不需要将列表作为令牌列表并且这是唯一的预期用途):
\documentclass{article}
\usepackage{enumitem}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { thanhph }
{
,1 .tl_set:N = \l__thanhph_title_tl
,2 .clist_set:N = \l__thanhph_list_clist
}
\NewDocumentCommand \TitleAndList {O{}}
{
\group_begin:
\keys_set:nn { thanhph } {#1}
\tl_if_empty:NF \l__thanhph_title_tl
{ \l__thanhph_title_tl :\par }
\clist_if_empty:NF \l__thanhph_list_clist
{ \__thanhph_typeset_list: }
\group_end:
}
\cs_new_protected:Npn \__thanhph_typeset_list:
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\item % one \item in front of the first clist element
\clist_use:Nn \l__thanhph_list_clist \item % and one \item in front of the rest
\end{itemize}
}
\ExplSyntaxOff
\newcommand\mylist{one item, another item}
\begin{document}
\TitleAndList[
1=Include,
2={{First item, with a comma}, {Second item}}
]
---
\TitleAndList[1=Not a list]
---
\TitleAndList[2={An item, another}]
\end{document}
看@egreg 的回答此方法的较短版本。
答案2
如果您希望将 clist 作为键的值,请按此方式定义它。
\documentclass{article}
\usepackage{enumitem}
%\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand\DrawList{m}
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\clist_map_inline:nn { #1 } { \item ##1 }
\end{itemize}
}
\keys_define:nn { thanhph/lists }
{
1 .tl_set:N = \l__thanhph_lists_title_tl,
2 .clist_set:N = \l__thanhph_lists_list_clist,
}
\NewDocumentCommand \TitleAndList {O{}}
{
\group_begin:
\keys_set:nn { thanhph/lists } { #1 }
\tl_if_empty:NF \l__thanhph_lists_title_tl
{
\l__thanhph_lists_title_tl \par
}
\clist_if_empty:NF \l__thanhph_lists_list_clist
{
\begin{itemize}[leftmargin=*, noitemsep, topsep=0pt]
\clist_map_inline:Nn \l__thanhph_lists_list_clist { \item ##1 }
\end{itemize}
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\subsubsection*{Just the list}
\DrawList{{First item, with a comma}, {Second item}}
\subsubsection*{Title and list}
\TitleAndList[
1=Include,
2={{First item, with a comma}, {Second item}}
]
\subsubsection*{Only title}
\TitleAndList[1=Not a list]
\subsubsection*{Only list}
\TitleAndList[2={An item,Another item}]
\end{document}