我实际上找到了一些解决方法并解决了我的问题。但我仍然无法理解一些要点。
我正在尝试编写一个命令来绘制表格。我从这个开始:
\documentclass[10pt]{article}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{hyperref}
\usepackage{fontawesome}
\ExplSyntaxOn
\tl_new:N \l_contact_tl
\newcommand \AddContact[2]{
\tl_put_right:Nn \l_contact_tl {#1}
\tl_put_right:Nn \l_contact_tl {}
\tl_put_right:Nn \l_contact_tl {\\}
}
\NewDocumentCommand \PrintContact {}{
\begin{tabular}{lr}
\tl_use:N \l_contact_tl
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\AddContact{ABC}{123}
\AddContact{\href{tel:+01 123-456-789}{+01 123-456-789}}{\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
\PrintContact
\end{document}
它完美地工作:
但是当我尝试键值命令时:
\documentclass[10pt]{article}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{hyperref}
\usepackage{fontawesome}
\ExplSyntaxOn
\tl_new:N \l_contact_tl
\keys_define:nn {contact}{
,info .tl_set:N = \l__contact_info_tl
,icon .tl_set:N = \l__contact_icon_tl
}
\NewDocumentCommand \AddContact {m}{
\keys_set:nn {contact}{#1}
\tl_put_right:Nn \l_contact_tl {\l__contact_info_tl}
\tl_put_right:Nn \l_contact_tl {& \l__contact_icon_tl}
\tl_put_right:Nn \l_contact_tl {\\}
}
\NewDocumentCommand \PrintContact {}{
\begin{tabular}{lr}
\tl_use:N \l_contact_tl
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={\href{tel:+01 123-456-789}{+01 123-456-789}},
icon={\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
}
\PrintContact
\end{document}
最后一条记录将覆盖以上所有记录:
当我更改为说明符时,它可以与不带andx
的普通文本一起使用:\href
\fontawesome
\NewDocumentCommand \AddContact {m}{
\keys_set:nn {contact}{#1}
\tl_put_right:Nx \l_contact_tl {\l__contact_info_tl}
\tl_put_right:Nx \l_contact_tl {& \l__contact_icon_tl}
\tl_put_right:Nx \l_contact_tl {\\}
}
...
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={XYZ},
icon={456}
}
\PrintContact
\end{document}
如果我发送一个图标、一个raisebox
、一个scalebox
或href
,它会返回某些类型的错误。
最后,我弄清楚了这项工作,但仍然不明白为什么:
\NewDocumentCommand \AddContact {m}{
\keys_set:nn {contact}{#1}
\tl_put_right:Nx \l_contact_tl {\unexpanded\expandafter{\l__contact_info_tl}}
\tl_put_right:Nx \l_contact_tl {& \unexpanded\expandafter{\l__contact_icon_tl}}
\tl_put_right:Nn \l_contact_tl {\\}
}
我觉得我误解了这里关于 token 扩展的一些关键点。有人对此有想法吗?谢谢。
答案1
一些解释
当您这样做时,\tl_put_right:Nn
第二个参数将按原样存储在您的标记列表中(不会发生任何扩展)。当您以这种方式将宏(或另一个标记列表)存储在第一个标记列表中时,它将仅包含宏,并且当您最终使用第一个标记列表时,内部标记列表将扩展为当时的替换。
因此,使用纯 TeX 语法进行的操作如下:
\def\foo{A}
\def\bar{\foo}
\def\foo{B}
使用时,这\bar
将扩展为\foo
,将扩展为B
。
正如您正确意识到的那样,您需要扩展内部标记列表。当您x
在expl3
语法中使用 -argument 时,这意味着“尽可能完全扩展”,但有些宏(很多,尤其是较旧的宏)不会保存在这样的上下文中。因此,当您在任意内容上运行它时,您永远不知道这是否有效。
然后你使用了。这将与“扩展一次”(语法中)\unexpanded\expandafter{<stuff>}
相同。这是因为扩展所有标记直到找到一个左括号,所以扩展将扩展在找到左括号之前的第一个标记,并保护该组中的所有内容免于进一步扩展(删除括号)。在语言中,执行相同操作的函数是。因此,你可以使用o
expl3
\unexpanded
\expandafter
<stuff>
\unexpanded
expl3
\exp_not:o
\tl_put_right:Nx \l_contact_tl
{
\exp_not:o { \l__contact_info_tl & }
\exp_not:o { \l__contact_icon_tl \\ }
}
一些代码
除了使用x
-type 扩展之外,您还可以仅扩展内部标记列表一次,\tl_put_right:No
而是使用:
\documentclass[10pt]{article}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{hyperref}
\usepackage{fontawesome}
\ExplSyntaxOn
\tl_new:N \l_contact_tl
\keys_define:nn {contact}{
,info .tl_set:N = \l__contact_info_tl
,icon .tl_set:N = \l__contact_icon_tl
}
\NewDocumentCommand \AddContact {m}{
\keys_set:nn {contact}{#1}
\tl_put_right:No \l_contact_tl {\l__contact_info_tl&}
\tl_put_right:No \l_contact_tl {\l__contact_icon_tl}
\tl_put_right:Nn \l_contact_tl {\\}
}
\NewDocumentCommand \PrintContact {}{
\begin{tabular}{lr}
\tl_use:N \l_contact_tl
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={\href{tel:+01 123-456-789}{+01 123-456-789}},
icon={\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
}
\PrintContact
\end{document}
但我建议进行一些小的调整:使您构建的令牌列表全局化,并将您的 key=value 解析放在一个组中,这样不同的调用就不会\AddContact
互相干扰(例如,如果您没有为一个联系人指定一个图标,它将使用以前在本地实现中使用的图标):
\documentclass[10pt]{article}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{hyperref}
\usepackage{fontawesome}
\ExplSyntaxOn
\tl_new:N \g_contact_tl
\keys_define:nn {contact}{
,info .tl_set:N = \l__contact_info_tl
,icon .tl_set:N = \l__contact_icon_tl
}
\NewDocumentCommand \AddContact {m}{
\group_begin:
\keys_set:nn {contact}{#1}
\tl_gput_right:No \g_contact_tl {\l__contact_info_tl&}
\tl_gput_right:No \g_contact_tl {\l__contact_icon_tl}
\tl_gput_right:Nn \g_contact_tl {\\}
\group_end:
}
\NewDocumentCommand \PrintContact {}{
\begin{tabular}{lr}
\tl_use:N \g_contact_tl
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={\href{tel:+01 123-456-789}{+01 123-456-789}},
icon={\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
}
\AddContact{
info=Stuff,
% no icon
}
\PrintContact
\end{document}
分组和使用全局列表的替代方法是使用\tl_clear:N \l__contact_info_tl \tl_clear:N \l__contact_icon_tl
before \keys_parse:nn
。
答案2
您想添加价值存储在令牌列表中。因此,您需要
\tl_put_right:NV \l__thanhph_contact_tl \l__thanhph_contact_info_tl
并不是\tl_put_right:Nn
。
我还会&
单独添加,因为它实际上不属于任何一个操作。
info
您还需要清除变量,否则如果命令icon
中未说明,您将得到以前的值\AddContact
。
\documentclass[10pt]{article}
\usepackage{xparse}
\usepackage{fontawesome}
\usepackage{hyperref}
\ExplSyntaxOn
% user interface
\NewDocumentCommand \AddContact {m}
{
\thanhph_contact_add:n { #1 }
}
\NewDocumentCommand \PrintContact {}
{
\begin{tabular}{lr}
\tl_use:N \l_thanhph_contact_tl
\end{tabular}
}
% variables
\tl_new:N \l_thanhph_contact_tl
\keys_define:nn {thanhph/contact}
{
info .tl_set:N = \l__thanhph_contact_info_tl ,
icon .tl_set:N = \l__thanhph_contact_icon_tl ,
}
% internal code
\cs_new_protected:Nn \thanhph_contact_add:n
{
\keys_set:nn {thanhph/contact}{info=,icon=,#1}
\tl_put_right:NV \l_thanhph_contact_tl \l__thanhph_contact_info_tl
\tl_put_right:Nn \l_thanhph_contact_tl { & }
\tl_put_right:NV \l_thanhph_contact_tl \l__thanhph_contact_icon_tl
\tl_put_right:Nn \l_thanhph_contact_tl { \\ }
}
\ExplSyntaxOff
\begin{document}
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={\href{tel:+01 123-456-789}{+01 123-456-789}},
icon={\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
}
\PrintContact
\end{document}
请注意,正确的命名方案需要更多内容;我使用您的名字作为第一部分,然后contact
作为模块名称。这将减少与其他人的代码发生冲突的机会。
您可以通过一个步骤添加所有内容:
\tl_put_right:Nx \l_thanhph_contact_tl
{
\exp_not:V \l__thanhph_contact_info_tl
&
\exp_not:V \l__thanhph_contact_icon_tl
\exp_not:N \\
}
但这需要对扩展进行更多的控制。使用\exp_not:V
,该值将被使用,但不会进一步扩展。
实际上,如果您需要对项目应用格式,我建议您采用这种方法。在这里,我将信息设为粗体,但对图标不做任何处理(这可以通过辅助函数轻松更改)。
\documentclass[10pt]{article}
\usepackage{graphicx}
\usepackage{fontawesome}
\usepackage{hyperref}
\ExplSyntaxOn
% user interface
\NewDocumentCommand \AddContact {m}
{
\thanhph_contact_add:n { #1 }
}
\NewDocumentCommand \PrintContact {}
{
\begin{tabular}{lr}
\tl_use:N \l_thanhph_contact_tl
\end{tabular}
}
% variables
\tl_new:N \l_thanhph_contact_tl
\keys_define:nn {thanhph/contact}
{
info .tl_set:N = \l__thanhph_contact_info_tl ,
icon .tl_set:N = \l__thanhph_contact_icon_tl ,
}
% internal code
\cs_new_protected:Nn \thanhph_contact_add:n
{
\keys_set:nn {thanhph/contact}{info=,icon=,#1}
\tl_put_right:Nx \l_thanhph_contact_tl
{
\__thanhph_contact_format_info:n { \exp_not:V \l__thanhph_contact_info_tl }
&
\__thanhph_contact_format_icon:n { \exp_not:V \l__thanhph_contact_icon_tl }
\exp_not:N \\
}
}
\cs_new_protected:Nn \__thanhph_contact_format_info:n { \textbf{#1} }
\cs_new_protected:Nn \__thanhph_contact_format_icon:n { #1 } % do nothing special
\ExplSyntaxOff
\begin{document}
\AddContact{
info={ABC},
icon={123}
}
\AddContact{
info={\href{tel:+01 123-456-789}{+01 123-456-789}},
icon={\raisebox{0.5pt}{\scalebox{0.75}{\faEnvelope}}}
}
\PrintContact
\end{document}