嵌套宏的扩展问题

嵌套宏的扩展问题

我正在尝试通过以下方式合并会员检查宏土拨鼠菲利佩·奥莱尼克(感谢他们两人提供的巧妙解决方案)变成了另一个宏的参数:

\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

这会导致以下错误消息:

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}

以下是演示此问题的 MWE。有人能帮我解决这个问题吗?

\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}

答案1

我们的常驻土拨鼠提出的宏很好,但不能使用,\fpeval因为它依赖于不可扩展的操作(\memberfalse\membertrue)。

标签\ExplSyntaxOn\ExplSyntaxOff类似于\makeatletter\makeatother:它们必须环绕使用该语法的代码expl3,而不是在代码内部。

更好的编码:

\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}

答案2

我的宏确实“不可扩展”我永远不会\fpeval在坐标中使用tikzpicture。这是因为 TiZ 有一个非常强大的解析器,因此你可以直接将表达式传递给 TiZ 会自动解析它们,所以你可以完全忽略所有这些“嘿,我的东西是可扩展的”讨论。特别是,声明一个可以像其他函数一样解析的函数相当简单。在我看来,生成的代码比普通 TimemberQ的疯狂混合要优雅得多\fpevalZ 解析。

\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}

附录:一个memberQ似乎适用于任意列表的函数。

\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}

相关内容