# Issue with Expansions of Nested Macros

I am trying to incorporate membership checking macros by marmot and Phelype Oleinik (thanks to both of them for their neat solutions) into an argument of another macro:

\singleBox{\convertNumPhelypeOleinik{1}{1}{0}{2}{1.46}}{200}{24}{13} % this fails
\singleBox{\convertNumMarmot{1}{1}{0}{2}{1.46}}{200}{24}{13} % this fails too


which results in the following error message:

Undefined control sequence. ...ypeOleinik{1}{1}{0}{2}{1.46}}{200}{24}{13}
Undefined control sequence. ...tNumMarmot{1}{1}{0}{2}{1.46}}{200}{24}{13}


The following is an MWE that demonstrates this issue. Could someone kindly help me with this?

\documentclass[tikz, border=0mm]{standalone}
\usepackage{tikz,xfp,expl3}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Solution by marmot https://tex.stackexchange.com/users/121799/marmot
% https://tex.stackexchange.com/a/501776/23594
\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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Solution by Phelype Oleinik https://tex.stackexchange.com/users/134574/phelype-oleinik
% https://tex.stackexchange.com/a/501777/23594
\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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\singleBox}[4]{%
\fill(\fpeval{#1}mm,\fpeval{#2}mm) rectangle
(\fpeval{#1} mm + \fpeval{#3} mm,\fpeval{#2} mm - \fpeval{#4} mm);
}

\newcommand{\convertNum}[5]{%
\fpeval{#1<6 ? #3 + 9 + (#4 + #5)*(#1-1) + (#2-1) : 0}
}

\newcommand{\convertNumPhelypeOleinik}[5] %
{
\ExplSyntaxOn
\fpeval{ \afp_int_ismember_p:nn {1} {1,2,3,4,5} ? #3 + 9 + (#4 + #5)*(#1-1) + (#2-1) : 0 }
\ExplSyntaxOff
}

\newcommand{\convertNumMarmot}[5] %
{
\MemberQ{1,2,3,4,5,6}{#1}
\ifmember \fpeval{#3 + 9 + (#4 + #5)*(#1-1) + (#2-1)} \fi
}

\begin{document}

% Testing membeship using Phelype Oleinik's method
\ExplSyntaxOn
\fpeval{ \afp_int_ismember_p:nn {1} {1,2,3,4,5} ? 123 : 321 }\par
\ExplSyntaxOff

% Testing membeship using marmot's method
\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

\begin{tikzpicture}
\draw[draw=black] (0mm,0mm) rectangle (300 mm, 400 mm);
\singleBox{100}{200}{24}{13} % This works just fine
\singleBox{\convertNum{1}{1}{0}{2}{1.46}}{200}{24}{13} % This work just fine

\singleBox{\convertNumPhelypeOleinik{1}{1}{0}{2}{1.46}}{200}{24}{13} % this fails
\singleBox{\convertNumMarmot{1}{1}{0}{2}{1.46}}{200}{24}{13} % this fails too
\end{tikzpicture}

\end{document}

• You're placing \ExplSyntaxOn and \ExplSyntaxOff in the wrong place; they should be around the definition of \convertNumPhelypeOleinik, not inside it. The macro proposed by marmot is not expandable, so it cannot appear in the argument to \fpeval. – egreg Jul 28 at 21:44
• @egreg You're the best! It worked beautifully. – AFP Jul 28 at 21:49

The macro proposed by our resident marmot is nice, but cannot be used in \fpeval as it relies to non expandable actions (\memberfalse and \membertrue).

The tags \ExplSyntaxOn and \ExplSyntaxOff are similar to \makeatletter and \makeatother: they have to surround the code where the expl3 syntax is used, not be inside the code.

A better coding:

\documentclass[border=0mm]{standalone}
\usepackage{tikz,xfp,xparse}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Solution by Phelype Oleinik https://tex.stackexchange.com/users/134574/phelype-oleinik
% https://tex.stackexchange.com/a/501777/23594
\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} }
}
\NewExpandableDocumentCommand{\convertNum}{mmmmm}
{
\fp_eval:n
{
\afp_int_ismember_p:nn {1} {1,2,3,4,5} ? #3 + 9 + (#4 + #5)*(#1-1) + (#2-1) : 0
}
}
\ExplSyntaxOff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\singleBox}[4]{%
\fill(\fpeval{#1}mm,\fpeval{#2}mm) rectangle
(\fpeval{#1} mm + \fpeval{#3} mm,\fpeval{#2} mm - \fpeval{#4} mm)
}

\begin{document}

\begin{tikzpicture}
\draw[draw=black] (0mm,0mm) rectangle (300 mm, 400 mm);
\singleBox{100}{200}{24}{13};
\singleBox{\convertNum{1}{1}{0}{2}{1.46}}{200}{24}{13};
\end{tikzpicture}

\end{document}


It is true that my macro is "not expandable" BUT I would never ever use \fpeval in coordinates of a tikzpicture. This is because TikZ has a very powerful parser, so you can just pass expressions to TikZ and it parses them automatically, so you can just ignore all this "hey, my stuff is expandable" discussion altogether. In particular, it is rather straightforward to declare a function memberQ that can be parsed like any other function. The resulting code is IMHO much more elegant than a wild mix of \fpeval and ordinary TikZ parsings.

\documentclass[tikz, border=0mm]{standalone}
\makeatletter
\pgfmathdeclarefunction{memberQ}{2}{%
\begingroup%
\edef\pgfutil@tmpb{0}%
\edef\pgfutil@tmpa{#2}%
\expandafter\pgfmath@member@i\pgfutil@firstofone#1\pgfmath@token@stop
\edef\pgfmathresult{\pgfutil@tmpb}%
\pgfmath@smuggleone\pgfmathresult%
\endgroup}
\def\pgfmath@member@i#1{%
\ifx\pgfmath@token@stop#1%
\else
\ifnum#1=\pgfutil@tmpa\relax%
\gdef\pgfutil@tmpb{1}%
%\typeout{#1=\pgfutil@tmpa}
\fi%
\expandafter\pgfmath@member@i
\fi}
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\singleBox}[4]{%
\fill({(#1)*1mm},{(#2)*1mm}) rectangle
({(#1+#3)*1mm},{(#2-#4)*1mm});
}

\begin{document}
\begin{tikzpicture}
\draw[draw=black] (0mm,0mm) rectangle (300 mm, 400 mm);
\singleBox{100}{200}{24}{13} % This works just fine

\pgfmathparse{memberQ({1,2,3,4,5,6,7,8,9,10,11},3)}
\typeout{\pgfmathresult}
\singleBox{ifthenelse(memberQ({1,2,3,5},3),200,300)}{200}{24}{13} % This works just fine
\end{tikzpicture}
\end{document}


ADDENDUM: a memberQ function that seems to work for arbitrary lists.

\documentclass[tikz, border=0mm]{standalone}
\makeatletter
\pgfmathdeclarefunction{memberQ}{2}{%
\begingroup%
\edef\pgfutil@tmpb{0}%
\edef\pgfutil@tmpa{#2}%
\expandafter\pgfmath@member@i\pgfutil@firstofone#1\pgfmath@token@stop
\edef\pgfmathresult{\pgfutil@tmpb}%
\pgfmath@smuggleone\pgfmathresult%
\endgroup}
\def\pgfmath@member@i#1{%
\ifx\pgfmath@token@stop#1%
\else
\edef\pgfutil@tmpc{#1}%
\ifx\pgfutil@tmpc\pgfutil@tmpa\relax%
\gdef\pgfutil@tmpb{1}%
%\typeout{#1=\pgfutil@tmpa}%
\fi%
\expandafter\pgfmath@member@i
\fi}
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\pgfmathparse{memberQ({3,4,5},3)}\pgfmathresult

\pgfmathparse{memberQ({3,4,5},1)}\pgfmathresult

\pgfmathparse{memberQ({"a","b","c"},1)}\pgfmathresult

\pgfmathparse{memberQ({"a","b","c"},"a")}\pgfmathresult
\end{document}

• I just said that your macro cannot go inside \fpeval, Expandability has certain advantages, but as TikZ is written, it is most of the time not required. – egreg Jul 28 at 22:47
• @egreg I agree on all this but my point is just that I would avoid mixing xfp and TikZ and would in particular not recommend it for newcomers. But it is true that outside of TikZ and friends all this xparse stuff is just great. – user121799 Jul 28 at 22:49
• Happy to see you came out of your burrow, by the way! – egreg Jul 28 at 22:51
• @egreg Thanks, but this is only till I awarded all my points, after that I will delete my account. (One can only waste up to 1500 points per day.) Until then, when trapped on a airport, I will occasionally answer questions. BTW, if you have recommendations for original answers that deserve extra points, I am all ears. – user121799 Jul 28 at 22:53
• @marmot Deleting your account? [Sobs] Is there any link I missed where you explain why, assuming you decided to publish it (if not, I'll respect your decision, of course)? Thanks for all your great contributions—that's invaluable. Whatever comes next, I wish you the best. – frougon Jul 29 at 10:29