有没有办法检查一个整数是否是整数列表的成员并返回布尔值?
我找到了这个巧妙的解决方案,这里效果很好。它检查字符串列表中是否存在某个字符串:
\documentclass{article} \usepackage{xstring} \newcommand\IfStringInList[2]{\IfSubStr{,#2,}{,#1,}} \begin{document} \IfStringInList{Paul}{George,John,Paul,Ringo}{True}{False} \end{document}
但是,它返回的是一个字符串,而不是布尔值“true”或“false”,这给我带来了一些困难,已发布这里。
让我们调用我想要的宏\ISMEMBER
。我的目标是使用\ISMEMBER
并检查整数是否在列表中,并根据它是否在列表中执行一些任务,例如:
\fpeval{ \ISMEMBER{1}{1,2,3,4,5} ? \MACRO_FOR_MEMBERS : \MACRO_FOR_NON_MEMBERS }
这样的功能可以实现吗?
答案1
您可以使用逗号分隔列表解析器来循环遍历项目列表并检查列表中是否存在给定项目。由于可以使用分隔宏来读取逗号分隔列表,因此可以扩展此操作,允许您插入宏\fpeval
以获取所需的语法。
使用expl3
(无论如何你都在加载它xfp
)类似这样的方法可以解决问题:
\ExplSyntaxOn
\prg_new_conditional:Npnn \afp_int_ismember:nn #1#2 { p, T, F, TF }
{ \__afp_ismember_loop:nw {#1} #2 , \q_recursion_tail , \q_recursion_stop }
\cs_new:Npn \__afp_ismember_loop:nw #1#2,
{
\quark_if_recursion_tail_stop_do:nn {#2}
{ \prg_return_false: }
\int_compare:nNnTF {#1} = {#2}
{ \use_i_delimit_by_q_recursion_stop:nw { \prg_return_true: } }
{ \__afp_ismember_loop:nw {#1} }
}
\ExplSyntaxOff
上面的代码定义了一个条件\afp_int_ismember:nn(TF)
,其第一个参数是要检查的项目,第二个参数是逗号分隔的列表。该宏首先展开\__afp_ismember_loop:nw
:此宏采用要测试的项目( )和列表中的第一个项目,以( )#1
分隔。该宏使用测试和的相等性,并发出它们是否相等或调用,
#2
#1
#2
\int_compare:nNnTF
\prg_return_true:
\__afp_ismember_loop:nw
,否则调用下一个项目。如果找到列表的末尾(IE, \q_recursion_tail
被抓取),那么该函数就会发出问题,\prg_return_false:
因为没有找到匹配项#1
。
上面的代码可以调整为\int_compare:nNnTF
通用相等比较函数。如果这样做,您可以定义包装器\__afp_ismember_loop:nw
来为不同的数据类型创建ismember
函数。下面的代码就是这样做的,并定义了两个函数:\afp_int_ismember:nn(TF)
(using \int_compare:nNnTF
) 和\afp_str_ismember:nn(TF)
(using \str_if_eq:eeTF
)。这样做你甚至可以测试我是否是披头士乐队的成员!
要使用该函数,您只需要条件函数的\fpeval
谓词形式( ):\afp_int_ismember_p:nn
\fpeval{ \afp_int_ismember_p:nn {1} {1,2,3,4,5} ? 123 : 321 }
字符串版本在这里也可以工作:
\fpeval{ \afp_str_ismember_p:nn {Phelype} {George,John,Paul,Ringo} ? 123 : 321 }
完整代码:
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{xfp}
\ExplSyntaxOn
% Core code for the membership test
\cs_new:Npn \__afp_ismember_loop:Nnw #1#2#3,
{
\quark_if_recursion_tail_stop_do:nn {#3}
{ \prg_return_false: }
#1 {#2} {#3}
{ \use_i_delimit_by_q_recursion_stop:nw { \prg_return_true: } }
{ \__afp_ismember_loop:Nnw #1 {#2} }
}
% Wrapper for testing integers
\prg_new_conditional:Npnn \afp_int_ismember:nn #1#2 { p, T, F, TF }
{
\__afp_ismember_loop:Nnw \__afp_int_isequal:nnTF {#1} #2 ,
\q_recursion_tail , \q_recursion_stop
}
\prg_new_conditional:Npnn \__afp_int_isequal:nn #1#2 { p, T, F, TF }
{
\int_compare:nNnTF {#1} = {#2}
{ \prg_return_true: }
{ \prg_return_false: }
}
% Wrappers for testing strings
% With expansion
\prg_new_conditional:Npnn \afp_str_ismember:ee #1#2 { p, T, F, TF }
{
\__afp_ismember_loop:Nnw \str_if_eq:eeTF {#1} #2 ,
\q_recursion_tail , \q_recursion_stop
}
% Without expansion
\prg_new_conditional:Npnn \afp_str_ismember:nn #1#2 { p, T, F, TF }
{
\__afp_ismember_loop:Nnw \str_if_eq:nnTF {#1} #2 ,
\q_recursion_tail , \q_recursion_stop
}
% Sample commands
\NewExpandableDocumentCommand { \IntIsmember } { m m }
{
\afp_int_ismember:nnTF {#1} {#2}
{ #1~is~member~of~`#2' }
{ #1~is~\emph{not}~member~of~`#2' }
}
\NewExpandableDocumentCommand { \StrIsmember } { m m }
{
\afp_str_ismember:nnTF {#1} {#2}
{ #1~is~member~of~`#2' }
{ #1~is~\emph{not}~member~of~`#2' }
}
\ExplSyntaxOff
\begin{document}
\IntIsmember{1}{1,2,3}
\IntIsmember{4}{1,2,3}
\StrIsmember{Paul}{George,John,Paul,Ringo}
\textbf{\StrIsmember{Phelype}{George,John,Paul,Ringo}}
\ExplSyntaxOn
\fpeval{ \afp_int_ismember_p:nn {1} {1,2,3,4,5} ? 123 : 321 }\par
\fpeval{ \afp_str_ismember_p:nn {Phelype} {George,John,Paul,Ringo} ? 123 : 321 }
\ExplSyntaxOff
\end{document}
答案2
像这样?宏名称的灵感来自 Mathematica 命令MemberQ
,代码来自这里. 此解决方案不需要任何包。
\documentclass{article}
\newif\ifmember
\makeatletter% for \@for see e.g. https://tex.stackexchange.com/a/100684/121799
%from https://tex.stackexchange.com/a/498576/121799
\newcommand{\MemberQ}[2]{\global\memberfalse%
\@for\next:=#1\do{\ifnum\next=#2\global\membertrue\fi}}
\makeatother
\begin{document}
\MemberQ{1,2,3,4}{2}
\ifmember 2 is in list \fi
\MemberQ{1,2,3,4}{5}
\ifmember 5 is in list\else%
5 is not in the list\fi
\end{document}