一些解释

一些解释

我实际上找到了一些解决方法并解决了我的问题。但我仍然无法理解一些要点。

我正在尝试编写一个命令来绘制表格。我从这个开始:

\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 {&#2}
    \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}

带有 x 说明符的普通文本

如果我发送一个图标、一个raisebox、一个scaleboxhref,它会返回某些类型的错误。

最后,我弄清楚了这项工作,但仍然不明白为什么:

\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

正如您正确意识到的那样,您需要扩展内部标记列表。当您xexpl3语法中使用 -argument 时,这意味着“尽可能完全扩展”,但有些宏(很多,尤其是较旧的宏)不会保存在这样的上下文中。因此,当您在任意内容上运行它时,您永远不知道这是否有效。

然后你使用了。这将与“扩展一次”(语法中)\unexpanded\expandafter{<stuff>}相同。这是因为扩展所有标记直到找到一个左括号,所以扩展将扩展在找到左括号之前的第一个标记,并保护该组中的所有内容免于进一步扩展(删除括号)。在语言中,执行相同操作的函数是。因此,你可以使用oexpl3\unexpanded\expandafter<stuff>\unexpandedexpl3\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_tlbefore \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}

在此处输入图片描述

相关内容