将 key=color (以逗号分隔)从 l3keys 传递到 tikz 时出现问题

将 key=color (以逗号分隔)从 l3keys 传递到 tikz 时出现问题

在论坛上搜索了好一会儿后,我放弃了(我想我可以自己做),我在将密钥从l3keys(以逗号分隔)传递给时遇到了问题tikz。我创建了一个接受逗号分隔的参数的命令,并key=val封装了tikz(mark)生成如下学校示例:

输出

命令如下

\tikzmkpd[config-A={yshift-A={#1}, end-yshift={#1,#2,#3},
                    color={#1,#2,#3}, yshift-A={#1}, distance={#1,#2,#3}
                    angle-star={#1,#2,#3}, angle-end={#1,#2,#3},
                    }]{A,B}{C,D,E}

接受以逗号分隔的参数的键都通过相同的函数传递,并且一切正常,除了color键之外,当激活时我收到错误:

! Package PGF Math Error: Unknown function `gray' (in 'gray').

See the PGF Math package documentation for explanation.
Type H <return> for immediate help.
 ...                                              

l.126 ...,yshift-A=15pt,color={gray,red}}]{A}{B,D}

该函数没有正确地划定界限,并且吸收了超过帐户的内容,将其余的参数传递给tikz,奇怪的是,如果我在\keys_define:nn其中定义相同的代码,它可以正常工作:

\keys_define:nn { tikzmkpd / config-A }
 {
  color .code:n = {
                   \clist_set:Nn \l_tmpa_clist { #1 }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 1 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                    }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 2 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                     \tl_set:Nx \l_colour_AD_tl { \clist_item:Nn \l_tmpa_clist {2} }
                    }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 3 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                     \tl_set:Nx \l_colour_AD_tl { \clist_item:Nn \l_tmpa_clist {2} }
                     \tl_set:Nx \l_colour_AE_tl { \clist_item:Nn \l_tmpa_clist {3} }
                    }
                  },
 }

这是我自己的代码,我尽可能地将其最小化,以便于理解(在我的原始文档中也有tikzmkpd / config-B

\documentclass{article}
\usepackage{xparse,tikz,etoolbox}
\usetikzlibrary{arrows.meta,bending,calc,tikzmark}
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}
\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\makeatletter
\def\@colon{:} % active : for expl3
\ExplSyntaxOn
\cs_new_protected:Npn \__define_numeric_keys:nnnnnnnnn #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tkmdraw/config-#1 }
   {
    yshift-#1C .tl_set:c = {l_yshift_#1C_tl}, yshift-#1C .initial:n = #2,
    yshift-#1D .tl_set:c = {l_yshift_#1D_tl}, yshift-#1D .initial:n = #3,
    yshift-#1E .tl_set:c = {l_yshift_#1E_tl}, yshift-#1E .initial:n = #4,
    distan-#1C .tl_set:c = {l_distan_#1C_tl}, distan-#1C .initial:n = #5,
    distan-#1D .tl_set:c = {l_distan_#1D_tl}, distan-#1D .initial:n = #6,
    distan-#1E .tl_set:c = {l_distan_#1E_tl}, distan-#1E .initial:n = #7,
    angles-#1C .tl_set:c = {l_angles_#1C_tl}, angles-#1C .initial:n = #8,
    angles-#1D .tl_set:c = {l_angles_#1D_tl}, angles-#1D .initial:n = #8,
    angles-#1E .tl_set:c = {l_angles_#1E_tl}, angles-#1E .initial:n = #8,
    anglee-#1C .tl_set:c = {l_anglee_#1C_tl}, anglee-#1C .initial:n = #9,
    anglee-#1D .tl_set:c = {l_anglee_#1D_tl}, anglee-#1D .initial:n = #9,
    anglee-#1E .tl_set:c = {l_anglee_#1E_tl}, anglee-#1E .initial:n = #9,
   }
}
\__define_numeric_keys:nnnnnnnnn{A}{1.7ex}{1.7ex}{1.7ex}{1ex}{2ex}{3ex}{60}{120}

\cs_new_protected:Npn \__define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tkmdraw/config-#1 }
   {
    colour-#1C .tl_set:c = {l_colour_#1C_tl}, colour-#1C .initial:n = #2,
    colour-#1D .tl_set:c = {l_colour_#1D_tl}, colour-#1D .initial:n = #3,
    colour-#1E .tl_set:c = {l_colour_#1E_tl}, colour-#1E .initial:n = #4,
   }
 }
\__define_colour_keys:nnnn{A}{red}{green}{blue}

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = {l_yshift_A_tl},%
  yshift-A   .initial:n = 1.7ex,
  end-yshift .code:n    = \__testkeys:nnn{A}{yshift}{#1},
  angle-star .code:n    = \__testkeys:nnn{A}{angles}{#1},
  angle-end  .code:n    = \__testkeys:nnn{A}{anglee}{#1},
  distance   .code:n    = \__testkeys:nnn{A}{distan}{#1},
  color      .code:n    = \__testkeys:nnn{A}{colour}{#1},
 }

\tl_new:N \l_tmpc_tl
\cs_new_protected:Npn \__testkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist { #3 }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nn { tkmdraw/config-#1 }
     { #2-#1C= \l_tmpa_tl, }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nn { tkmdraw/config-#1 }
     { #2-#1C= \l_tmpa_tl, #2-#1D= \l_tmpb_tl, }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l_tmpc_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nn { tkmdraw/config-#1 }
    { #2-#1C= \l_tmpa_tl, #2-#1D= \l_tmpb_tl, #2-#1E= \l_tmpc_tl,}
   }
 }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \_case_one:n
 {
  \draw[->,\use:c{l_colour_AC_tl}]
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AC_tl},in=\use:c{l_anglee_AC_tl},distance=\use:c{l_distan_AC_tl}]
   ([yshift=\use:c{l_yshift_AC_tl}]$(pic ~ cs\@colon\csuse{B1})$);
  \draw[->,\use:c{l_colour_AD_tl}] %,
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AD_tl},in=\use:c{l_anglee_AD_tl},distance=\use:c{l_distan_AD_tl}]
   ([yshift=\use:c{l_yshift_AD_tl}]$(pic ~ cs\@colon\csuse{B2})$);
 }
% case 2: a(c+d+e)  A1(B1+B2+B3)
\cs_new_protected:Npn \_case_two:n
 {
  \_case_one:n
  \draw[->,\use:c{l_colour_AE_tl}]
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AE_tl},in=\use:c{l_anglee_AE_tl},distance=\use:c{l_distan_AE_tl}]
   ([yshift=\use:c{l_yshift_AE_tl}]$(pic ~ cs\@colon\csuse{B3})$);
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } { #1 } },
 }

\NewDocumentCommand{\tikzmkpd}{O{} m m}
 {
  \group_begin:
  \IfNoValueF { #1 } { \keys_set:nn { tikzmkpd }{ #1 } }
   \foreach \x [count=\n] in {#2} { \csxdef{A\n}{\x} }%  save in A<n>
   \foreach \y [count=\m] in {#3} { \csxdef{B\m}{\y} }%  save in B<m>
   \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}
    \begin{tikzpicture}[overlay,remember ~ picture]%
     \bool_if:nT { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
                   \int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_one:n }
     \bool_if:nT { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
                   \int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_two:n }
    \end{tikzpicture}
  \group_end:
 }
\ExplSyntaxOff
\makeatother
\begin{document}
\section{case 1}
$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={end-yshift={15pt,15pt},yshift-A=15pt,color={gray,red}}]{A}{B,D}
\section{case 2}
$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$
\tikzmkpd[config-A={color={gray,gray,gray},end-yshift={5pt,5pt,5pt}}]{a1}{b1,c1,d1}
\end{document}

有什么想法可以解决它吗?......(我的想法是尽量少重复类似的代码)

问候。

答案1

这不是一个最小的例子,这里有十个不同的问题!下面给出的代码有效。按照请求你的评论,为了更好地遵守 LaTeX3 编码约定(据我所知),我做出了比严格必要的更多的更改。

请注意,_private_中的命名方案\keys_define:nn { tikzmkpd/_private_/config-#1 }是我发明的;我不知道是否存在针对这种情况的标准命名方案。我这样做是因为我相信在此层次结构下定义的键是宏的实现细节,不应由“用户”直接使用。

LaTeX3 编码约定如下expl3.pdfl3styleguide.pdf界面3.pdf。那里建议的缩进样式与您的略有不同(特别是,它使用了两个空格的基本偏移量)。但下面的代码已经相当易读,所以我让您自己决定是否也严格遵循缩进指南。

对于函数,我使用了:

  • \__tikzmkpd_私有的前缀;

  • \tikzmkpd_公共的前缀(仅,我将其添加为与您已经定义的\tikzmkpd:nnn用户层命令相对应的编程层后端函数)。\tikzmkpd

这样,就可以很容易地将所有这些代码转换为一个可以与 LaTeX 生态系统的其余部分很好地兼容的包。您的\_case_one:n\_case_two:n函数名称不正确,原因有两个:

  • 前缀;
  • 后缀:n表示他们应该接受论点,但实际上却没有。

我将这两个函数分别重命名为\__tikzmkpd_case_one:\__tikzmkpd_case_two:

\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, bending, calc, tikzmark}

\ExplSyntaxOn

\cs_new_protected:Npn \__tikzmkpd_define_numeric_keys:nnnnnnnnn
                      #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    yshift-#1C .tl_set:c = { l_yshift_#1C_tl }, yshift-#1C .initial:n = {#2},
    yshift-#1D .tl_set:c = { l_yshift_#1D_tl }, yshift-#1D .initial:n = {#3},
    yshift-#1E .tl_set:c = { l_yshift_#1E_tl }, yshift-#1E .initial:n = {#4},
    distan-#1C .tl_set:c = { l_distan_#1C_tl }, distan-#1C .initial:n = {#5},
    distan-#1D .tl_set:c = { l_distan_#1D_tl }, distan-#1D .initial:n = {#6},
    distan-#1E .tl_set:c = { l_distan_#1E_tl }, distan-#1E .initial:n = {#7},
    angles-#1C .tl_set:c = { l_angles_#1C_tl }, angles-#1C .initial:n = {#8},
    angles-#1D .tl_set:c = { l_angles_#1D_tl }, angles-#1D .initial:n = {#8},
    angles-#1E .tl_set:c = { l_angles_#1E_tl }, angles-#1E .initial:n = {#8},
    anglee-#1C .tl_set:c = { l_anglee_#1C_tl }, anglee-#1C .initial:n = {#9},
    anglee-#1D .tl_set:c = { l_anglee_#1D_tl }, anglee-#1D .initial:n = {#9},
    anglee-#1E .tl_set:c = { l_anglee_#1E_tl }, anglee-#1E .initial:n = {#9},
   }
}

\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { A } { 1.7ex } { 1.7ex } { 1.7ex } { 1ex } { 2ex } { 3ex } { 60 } { 120 }
\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { B }{ -0.5ex }{ -0.5ex }{ -0.5ex } { 1ex } { 2ex } { 3ex } {-60 } {-120 }

\cs_new_protected:Npn \__tikzmkpd_define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    colour-#1C .tl_set:c = { l_colour_#1C_tl }, colour-#1C .initial:n = {#2},
    colour-#1D .tl_set:c = { l_colour_#1D_tl }, colour-#1D .initial:n = {#3},
    colour-#1E .tl_set:c = { l_colour_#1E_tl }, colour-#1E .initial:n = {#4},
   }
 }

\__tikzmkpd_define_colour_keys:nnnn { A } { red } { green } { blue }
\__tikzmkpd_define_colour_keys:nnnn { B } { red } { green } { blue }

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = { l_yshift_A_tl },
  yshift-A   .initial:n = { 1.7ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { colour } {#1} },
  raise      .meta:n    = { yshift-A = {#1}, end-yshift = {#1,#1,#1} },
 }

\keys_define:nn { tikzmkpd / config-B }
 {
  yshift-B   .tl_set:c  = { l_yshift_B_tl },
  yshift-B   .initial:n = { -0.5ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { colour } {#1} },
  raise      .meta:n    = { yshift-B = {#1}, end-yshift = {#1,#1,#1} },
 }

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

\cs_new:Npn \__tikzmkpd_subs_ii:nnnn #1#2#3#4
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4} } }

\cs_new:Npn \__tikzmkpd_subs_iii:nnnnn #1#2#3#4#5
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4}, #1-#2E= {#5} } }

\cs_generate_variant:Nn \__tikzmkpd_subs_i:nnn { nnV }
\cs_generate_variant:Nn \__tikzmkpd_subs_ii:nnnn { nnVV }
\cs_generate_variant:Nn \__tikzmkpd_subs_iii:nnnnn { nnVVV }
\cs_generate_variant:Nn \keys_set:nn { nx }

\tl_new:N \l__tikzmkpd_tmp_tl

\cs_new_protected:Npn \__tikzmkpd_set_subkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist {#3}
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_i:nnV {#2} {#1} \l_tmpa_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_ii:nnVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l__tikzmkpd_tmp_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_iii:nnVVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl
                                  \l__tikzmkpd_tmp_tl }
   }
 }

\seq_new:N \l__tikzmkpd_mycmd_seq

\cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1#2
  {
    \seq_set_from_clist:Nn \l__tikzmkpd_mycmd_seq {#2}
    \seq_indexed_map_inline:Nn \l__tikzmkpd_mycmd_seq
      {
        \tl_clear_new:c { l__tikzmkpd_nodename_#1##1_tl }
        \tl_set:cn { l__tikzmkpd_nodename_#1##1_tl } {##2}
      }
  }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_one:
 {
  \draw[->, \use:c{l_colour_AC_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AC_tl}, in=\use:c{l_anglee_AC_tl},
      distance=\use:c{l_distan_AC_tl}]
   ([yshift=\use:c{l_yshift_AC_tl}] \tl_use:c { l__tikzmkpd_nodename_B1_tl });
  \draw[->,\use:c{l_colour_AD_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AD_tl}, in=\use:c{l_anglee_AD_tl},
      distance=\use:c{l_distan_AD_tl}]
   ([yshift=\use:c{l_yshift_AD_tl}] \tl_use:c { l__tikzmkpd_nodename_B2_tl });
 }

% case 2: a(c+d+e)  A1(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_two:
 {
  \__tikzmkpd_case_one:
  \draw[->, \use:c{l_colour_AE_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AE_tl}, in=\use:c{l_anglee_AE_tl},
      distance=\use:c{l_distan_AE_tl}]
   ([yshift=\use:c{l_yshift_AE_tl}] \tl_use:c { l__tikzmkpd_nodename_B3_tl });
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } {#1} },
  config-B .code:n = { \keys_set:nn { tikzmkpd / config-B } {#1} },
 }

\cs_new_protected:Npn \tikzmkpd:nnn #1#2#3
 {
  \group_begin:
  \keys_set:nn { tikzmkpd } {#1}
  \__tikzmkpd_store_node_names:nn {A} {#2}
  \__tikzmkpd_store_node_names:nn {B} {#3}
  \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}

  \begin{tikzpicture}[overlay, remember ~ picture]
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_one: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_two: }
  \end{tikzpicture}
  \group_end:
 }

\NewDocumentCommand \tikzmkpd { O{} m m }
 {
  \tikzmkpd:nnn {#1} {#2} {#3}
 }

\ExplSyntaxOff

\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}

\begin{document}

\section{case 1}

$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={end-yshift={8pt,9pt}, yshift-A=8pt, color={gray,red}}]%
         {A}{B, D}

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$%
\tikzmkpd[config-A={color={gray,gray,gray}, end-yshift={5pt,5pt,5pt}}]%
         {a1}{b1, c1, d1}

\end{document}

我测试了上述代码,它给出了下面的截图。在文章的最后,你会发现一个改进的 Pablo 变体,它处理了更多乘法分配律的情况(“一”到“四”的情况,而不仅仅是“一”和“二”的情况)。然而,我自己无法完全验证这个版本,因为它显然依赖于 PGF/Ti 的最新错误修复或功能Z—对于我这里的版本来说太新了,即2019/02/02 v3.1.1

截屏

我相信你可以在调试方法方面取得进展:你是否测试过你的每一个功能,你会这样做:

\tl_show:N \l_yshift_AC_tl
\tl_show:N \l_yshift_AD_tl
\tl_show:N \l_colour_AC_tl
\tl_show:N \l_colour_AD_tl

调用之后\keys_set:nn { tikzmkpd } {#1},这表明你的\__testkeys:nnn(现在调用的\__tikzmkpd_set_subkeys:nnn)没有正常工作。主要问题是你传递了单个令牌,例如\l_tmpa_tl在参数中,而你实际上希望它们的价值(相当于扩展\l_tmpa_tl或的结果\tl_use:N \l_tmpa_tl;看看我怎么做的)。此外,在某些地方,如果某些参数值包含逗号,则需要更多括号。例如,这个:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { #1-#2C = {#3}, }

比这个更好:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { #1-#2C= #3, }

以防万一#3包含逗号。事实上,除非你打算在#1,#2或中使用宏#3,否则以下做法可能更可取,以避免不必要的扩展(因为我们稍后会在参数中使用它x),这就是我上面所做的:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

另一个导致代码无法运行的问题是,对于使用 创建的节点\tikzmarknodepic cs您使用的语法不起作用 — 至少在我的设置中是这样。好消息是,这可以大大简化(请参阅我对 和\_case_one:n的更改\_case_two:n[现在重命名为\__tikzmkpd_case_one:\__tikzmkpd_case_two:])。

你的问题太复杂了,有那么多参数,TikZ 代码和 代码混在一起l3keys,我担心没人会重复使用我的答案。:-( 我并不批评使用 来l3keys进行 TikZ 工作(首先,我自己用 比 更舒服l3keyspgfkeys,但为了这个网站其他用户的利益,我认为你应该把解析问题和 TikZ 问题区分开来——因为人们已经告诉过你了

你的 LaTeX 编码技能看起来不错;我的主要建议是:

  • 阅读更多文档,特别是有关 LaTeX3 命名约定、扩展和变体的文档(请参阅界面3.pdf)。

  • 每当你再次遇到这样的问题时,请使用\tracingmacros=1 \tracingonline=1\relax,如果这还不足以理解发生了什么,请打印你的函数准备的每个“东西”的值1(针对你的每个函数)。例如,在尝试排版 之前,打印你将在其中使用的所有选项和所有参数。这种方法可以帮助你找到你的(现在重命名为)tikzpicture中的错误。\__testkeys:nnn\__tikzmkpd_set_subkeys:nnn

正如承诺的那样,以下是 Pablo 添加的扩展代码:

\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, bending, calc, tikzmark}

\ExplSyntaxOn

\cs_new_protected:Npn \__tikzmkpd_define_numeric_keys:nnnnnnnnn
                      #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    yshift-#1C .tl_set:c = { l_yshift_#1C_tl }, yshift-#1C .initial:n = {#2},
    yshift-#1D .tl_set:c = { l_yshift_#1D_tl }, yshift-#1D .initial:n = {#3},
    yshift-#1E .tl_set:c = { l_yshift_#1E_tl }, yshift-#1E .initial:n = {#4},
    distan-#1C .tl_set:c = { l_distan_#1C_tl }, distan-#1C .initial:n = {#5},
    distan-#1D .tl_set:c = { l_distan_#1D_tl }, distan-#1D .initial:n = {#6},
    distan-#1E .tl_set:c = { l_distan_#1E_tl }, distan-#1E .initial:n = {#7},
    angles-#1C .tl_set:c = { l_angles_#1C_tl }, angles-#1C .initial:n = {#8},
    angles-#1D .tl_set:c = { l_angles_#1D_tl }, angles-#1D .initial:n = {#8},
    angles-#1E .tl_set:c = { l_angles_#1E_tl }, angles-#1E .initial:n = {#8},
    anglee-#1C .tl_set:c = { l_anglee_#1C_tl }, anglee-#1C .initial:n = {#9},
    anglee-#1D .tl_set:c = { l_anglee_#1D_tl }, anglee-#1D .initial:n = {#9},
    anglee-#1E .tl_set:c = { l_anglee_#1E_tl }, anglee-#1E .initial:n = {#9},
   }
}

\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { A } { 1.7ex } { 1.7ex } { 1.7ex } { 1ex } { 2ex } { 3ex } { 60 } { 120 }
\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { B }{ -0.5ex }{ -0.5ex }{ -0.5ex } { 1ex } { 2ex } { 3ex } {-60 } {-120 }

\cs_new_protected:Npn \__tikzmkpd_define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    colour-#1C .tl_set:c = { l_colour_#1C_tl }, colour-#1C .initial:n = {#2},
    colour-#1D .tl_set:c = { l_colour_#1D_tl }, colour-#1D .initial:n = {#3},
    colour-#1E .tl_set:c = { l_colour_#1E_tl }, colour-#1E .initial:n = {#4},
   }
 }

\__tikzmkpd_define_colour_keys:nnnn { A } { red } { green } { blue }
\__tikzmkpd_define_colour_keys:nnnn { B } { red } { green } { blue }

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = { l_yshift_A_tl },
  yshift-A   .initial:n = { 1.7ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { colour } {#1} },
  raise      .meta:n    = { yshift-A = {#1}, end-yshift = {#1,#1,#1} },
 }

\keys_define:nn { tikzmkpd / config-B }
 {
  yshift-B   .tl_set:c  = { l_yshift_B_tl },
  yshift-B   .initial:n = { -0.5ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { colour } {#1} },
  raise      .meta:n    = { yshift-B = {#1}, end-yshift = {#1,#1,#1} },
 }

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

\cs_new:Npn \__tikzmkpd_subs_ii:nnnn #1#2#3#4
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4} } }

\cs_new:Npn \__tikzmkpd_subs_iii:nnnnn #1#2#3#4#5
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4}, #1-#2E= {#5} } }

\cs_generate_variant:Nn \__tikzmkpd_subs_i:nnn { nnV }
\cs_generate_variant:Nn \__tikzmkpd_subs_ii:nnnn { nnVV }
\cs_generate_variant:Nn \__tikzmkpd_subs_iii:nnnnn { nnVVV }
\cs_generate_variant:Nn \keys_set:nn { nx }

\tl_new:N \l__tikzmkpd_tmp_tl

\cs_new_protected:Npn \__tikzmkpd_set_subkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist {#3}
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_i:nnV {#2} {#1} \l_tmpa_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_ii:nnVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l__tikzmkpd_tmp_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_iii:nnVVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl
                                  \l__tikzmkpd_tmp_tl }
   }
 }

\seq_new:N \l__tikzmkpd_mycmd_seq

\cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1#2
  {
    \seq_set_from_clist:Nn \l__tikzmkpd_mycmd_seq {#2}
    \seq_indexed_map_inline:Nn \l__tikzmkpd_mycmd_seq
      {
        \tl_clear_new:c { l__tikzmkpd_nodename_#1##1_tl }
        \tl_set:cn { l__tikzmkpd_nodename_#1##1_tl } {##2}
      }
  }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_one:
 {
  \draw[->, \tl_use:c{l_colour_AC_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs  \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AC_tl}, in=\tl_use:c{l_anglee_AC_tl},
      distance=\tl_use:c{l_distan_AC_tl}]
   ([yshift=\tl_use:c{l_yshift_AC_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B1_tl})$);
  \draw[->,\tl_use:c{l_colour_AD_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs  \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AD_tl}, in=\tl_use:c{l_anglee_AD_tl},
      distance=\tl_use:c{l_distan_AD_tl}]
   ([yshift=\tl_use:c{l_yshift_AD_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B2_tl})$);
 }

% case 2: a(c+d+e)  A1(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_two:
 {
  \__tikzmkpd_case_one:
  \draw[->, \tl_use:c{l_colour_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AE_tl}, in=\tl_use:c{l_anglee_AE_tl},
      distance=\tl_use:c{l_distan_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_AE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
 }

% case 3: (a+b)(c+d) (A1+A2)(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_three:
 {
  \__tikzmkpd_case_one:
  \draw[->,\tl_use:c{l_colour_BC_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BC_tl},in=\tl_use:c{l_anglee_BC_tl},
      distance=\tl_use:c{l_distan_BC_tl}]
   ([yshift=\tl_use:c{l_yshift_BC_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B1_tl})$);
  \draw[->,\tl_use:c{l_colour_BD_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BD_tl},in=\tl_use:c{l_anglee_BD_tl},
      distance=\tl_use:c{l_distan_BD_tl}]
   ([yshift=\tl_use:c{l_yshift_BD_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B2_tl})$);
 }

% case 4: (a+b)(c+d+e) (A1+A2)(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_four:
 {
  \__tikzmkpd_case_three:
  \draw[->,\tl_use:c{l_colour_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AE_tl},in=\tl_use:c{l_anglee_AE_tl},
      distance=\tl_use:c{l_distan_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_AE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
  \draw[->,\tl_use:c{l_colour_BE_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BE_tl},in=\tl_use:c{l_anglee_BE_tl},
      distance=\tl_use:c{l_distan_BE_tl}]
   ([yshift=\tl_use:c{l_yshift_BE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } {#1} },
  config-B .code:n = { \keys_set:nn { tikzmkpd / config-B } {#1} },
 }

\cs_new_protected:Npn \tikzmkpd:nnn #1#2#3
 {
  \group_begin:
  \keys_set:nn { tikzmkpd } {#1}
  \__tikzmkpd_store_node_names:nn {A} {#2}
  \__tikzmkpd_store_node_names:nn {B} {#3}
  \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}

  \begin{tikzpicture}[overlay, remember ~ picture]
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_one: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_two: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 2 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_three: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 2 }   &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_four: }
  \end{tikzpicture}
  \group_end:
 }

\NewDocumentCommand \tikzmkpd { O{} m m }
 {
  \tikzmkpd:nnn {#1} {#2} {#3}
 }

\ExplSyntaxOff

\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}

\begin{document}

\section{case 1}

$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={raise=12pt}]%
         {A}{B, D}

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$%
\tikzmkpd[config-A={color={gray,gray,gray}, end-yshift={5pt,5pt,5pt}}]%
         {a1}{b1, c1, d1}

\section{case 3}

$(\TkM{X1}{2a^2}+\TkM{X2}{n})(\TkM{Y1}{b}+\TkM{Y2}{3q})$%
\tikzmkpd{X1,X2}{Y1,Y2}

\section{case 4}

$(\TkM{a}{x}-\TkM{b}{2})(\TkM{c}{x^2}+\TkM{d}{2x}+\TkM{e}{4})$
\tikzmkpd[config-B={end-yshift={-15pt,-15pt},color={gray,red}}]{a,b}{c, d, e}

\tikzmkpd[config-A={raise=4pt,color={gray,gray,gray}}]%
         {A}{B, D}
\end{document}

截屏


脚注

  1. 对于这样的临时代码,您可以使用 TeX 或 LaTeX2e 命令(例如\show或 )\typeout,以及 LaTeX3 命令(例如\tl_show:N或 )\box_show:N

相关内容