获取宏参数的第一个标记很容易。忽略一些不相关的复杂情况,您可以执行以下操作:
\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}