获取宏参数的最后一个标记

获取宏参数的最后一个标记

获取宏参数的第一个标记很容易。忽略一些不相关的复杂情况,您可以执行以下操作:

\def\firsttokof#1{\first(#1)}
\def\first(#1#2){#1}

同样容易获得之后所有剩余的代币第一个:

\def\nonfirsttoksof#1{\rest(#1)}
\def\rest(#1#2){#2}

据我了解,这是因为 TeX 从左到右进行模式匹配,采用最短的匹配。在 \first 和 \rest 中,#1 的最短匹配始终是单个标记。

但你怎样才能得到最后的宏参数的标记?

答案1

假设您只需要无参数的宏,这里有一种可以识别尾随空格或闭括号的方法。

\documentclass{article}
\usepackage{expl3,l3regex}

\def\A{abc}
\def\B{ab{c}}
\def\C{ab{c} }
\def\D{ab\linebreak}

\ExplSyntaxOn
\cs_new_protected:Npn \velleman_grab_last:N #1
 {
  \tl_set:Nx \l_velleman_testz_tl { \token_get_replacement_spec:N #1 }
  \tl_set_eq:NN \l_velleman_testy_tl \l_velleman_testz_tl
  \regex_replace_once:nnN { \A .* (.) \Z } { \1 } \l_velleman_testz_tl
  \regex_replace_once:nnN { \A .* (.) . \Z } { \1 } \l_velleman_testy_tl
  \prg_case_str:xxn { \l_velleman_testz_tl }
   {
    { \c_rbrace_str }{ \tl_set:Nn \l_velleman_last_tl { \c_group_end_token } }
    { \c_space_tl }{ \tl_set:Nn \l_velleman_last_tl { \c_space_token } \velleman_test_last:N #1 }
   }
   {
    \tl_set:Nx \l_velleman_last_tl { \tl_item:Vn #1 { -1 } }
   }
  \tl_show:N \l_velleman_last_tl
 }
\cs_new:Npn \velleman_test_last:N #1
 {
  \str_if_eq:xxF { \l_velleman_testy_tl } { \c_rbrace_str }
   {
    \tl_set:Nx \l_velleman_testz_tl { \tl_item:Vn #1 { -1 } }
    \token_if_cs:VT \l_velleman_testz_tl { \tl_set:NV \l_velleman_last_tl \l_velleman_testz_tl }
   }
 }

\cs_generate_variant:Nn \tl_item:nn {V}
\cs_generate_variant:Nn \token_if_cs:NT {V}

\velleman_grab_last:N \A
\velleman_grab_last:N \B
\velleman_grab_last:N \C
\velleman_grab_last:N \D

\ExplSyntaxOff

借助于,\regex_replace_once:nnN我们保留\l_velleman_test控制序列“含义”中的最后一项。然后我们整理出各种情况。如果最后一项是空格,则必须进行另一次检查;我们还保留倒数第二项;如果它是括号,那么最后一项肯定是空格;否则我们查看它是否是控制序列。

\space还有一些小问题,但这应该足以开始。例如,如果尾随空格跟在控制符号后面,宏就不起作用,最后的测试也不准确。

答案2

尽管我对 egreg 的令人印象深刻的回答表示充分的尊重,但我认为反转标记列表并获取现在第一个参数可能会稍微容易一些,尽管不那么通用:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \my_tl_last:N #1
  {
    \tl_reverse:N #1
    \tl_head:N #1
  }
\tl_set:Nn \l_tmpa_tl {ab{cd}ef}
\my_tl_last:N \l_tmpa_tl
\ExplSyntaxOff
\end{document}

根据您要查找的内容,存储结果可能需要稍作修改……

答案3

这个答案基于 egreg 的简化版本。如果想要获取标记列表中的最后一项(括号组或单个非空格非 [begin/end] 组标记),只需使用\tl_item:Nn \foo { -1 }。如果想要获取最后一个标记,最简单的方法是使用(实验性的)l3regex 模块,如 egreg 所述。在这里我定义了\velleman_get_last:nN,它需要两个参数:一些标记和一个用于存储结果的控制序列。

在大多数情况下,\regex_extract_once:nnN { . \Z } { <tokens> } \result可以解决问题:正则表达式表示“任何标记(.),后跟\Z输入的结尾( )”。下面的行将结果\regex_extract_once:nnN(当前为序列)转换为标记列表。唯一需要特殊处理的情况是最后一个标记是显式结束组字符。这不能放入宏中以给出结果:我们用 进行测试\regex_match:nnTF { \cE. \Z },其中正则表达式表示“具有任意字符代码( )的 catcode(\c)结束组(E)标记.,后跟\Z输入的结尾( ),在这种情况下,我们将\c_group_end_token隐式结束组标记 放入标记列表中。

\documentclass{article}
\usepackage{expl3,l3regex,l3str}

\def\A{abc}
\def\B{ab{c}}
\def\C{ab{c} }
\def\D{ab\linebreak}

\ExplSyntaxOn
%
\seq_new:N \l_velleman_last_seq
\tl_new:N \l_velleman_last_tl
\cs_new_protected:Npn \velleman_get_last:nN #1#2
  {
    \regex_match:nnTF { \cE. \Z } {#1}
      { \tl_set:Nn #2 { \c_group_end_token } }
      {
        \regex_extract_once:nnN { . \Z } {#1} \l_velleman_last_seq
        \tl_set:Nx #2 { \seq_item:Nn \l_velleman_last_seq { 1 } }
      }
  }
\cs_generate_variant:Nn \velleman_get_last:nN { V }
%
\cs_new_protected:Npn \test:N #1
  {
    \velleman_get_last:VN #1 \l_tmpa_tl
    \msg_term:n
      {
        Last ~ item: ~ ' \tl_item:Nn #1 { -1 } ' \\
        Last ~ token: ~ ' \tl_to_str:N \l_tmpa_tl '
      }
  }
\test:N \A
\test:N \B
\test:N \C
\test:N \D
\ExplSyntaxOff

\stop

答案4

\getlastarg@lastarg要从参数列表中获取最后一个参数,请注意找到的参数由(在临时组堆栈上)保留:

\documentclass{article}

%------------------------------------------------------------------------------
%getlastargument
\makeatletter

\def\getlastarg#1{%
  \def\getlastarg@lastarg{}%
  \getlastarg@parse#1\@@@getlastarg@etokenlist
}

\def\@@@getlastarg@etokenlist{%
  \message{^^J!!Expanding macro `\noexpand\@@@getlastarg@etokenlist',
    be sure not to use it explicitly!!^^J}}
\def\getlastarg@parse{%
  \futurelet\@let@token\getlastarg@ifetokenlist
}
\def\getlastarg@ifetokenlist{%
  \ifx\@let@token\@@@getlastarg@etokenlist
    \let\next=\getlastarg@lastarg
  \else
    \let\next=\getlastarg@gobble
  \fi
  \next}
\def\getlastarg@gobble#1{%
  \def\getlastarg@lastarg{#1}%
  \getlastarg@parse}

要从标记列表中获取最后一个标记(标记列表应包装在{,}对中,并且最后找到的标记由 保留\getlasttoken@lasttoken。如果标记列表的最后一个标记是<right brace>\getlasttoken@lasttoken则可能无法访问):

\documentclass{article}

%------------------------------------------------------------------------------
%getlastargument
\makeatletter

\def\getlasttoken#1{%
  \getlasttoken@parse#1%
  \getlasttoken@e
}

\def\getlasttoken@e*getlasttoken*endtokenlist{}
\def\getlasttoken@parse{%
  \afterassignment\getlasttoken@ifetokenlist\let\@let@token= }
\def\getlasttoken@ifetokenlist{%
  \ifx\@let@token\getlasttoken@e
    \let\next=\getlasttoken@lasttoken
  \else
    \let\getlasttoken@lasttoken=\@let@token
    \let\next=\getlasttoken@parse
  \fi
  \next}

相关内容