l3prop 属性存储和 etoolbox 循环:奇怪的结果

l3prop 属性存储和 etoolbox 循环:奇怪的结果

我正在编写一个处理多语言术语公式的l3prop类。我的想法是使用一种哈希表来存储每个公式的语言变体。基于 为变量赋值并检索以供日后使用的推荐方法是什么?,我正在尝试以下操作:

\documentclass{article}
\usepackage{expl3}
\usepackage{etoolbox}
\usepackage{xstring}

\ExplSyntaxOn
\newcommand*{\setproperty}[3][standard]{%
  \cs_if_exist:cF { g_citr_#1_prop } { \prop_new:c { g_citr_#1_prop } }
  \prop_gput:cnn { g_citr_#1_prop } { #2 } { #3 }
}
\newcommand*{\getproperty}[2][standard]{%
  \cs_if_exist:cTF { g_citr_#1_prop } {%
    \prop_get:cnN { g_citr_#1_prop } { #2 } \l_citr_value_tl
    \quark_if_no_value:NTF \l_citr_value_tl {%
      Inexistent~property~`#2'
    }{%
      \tl_use:N \l_citr_value_tl
    }
  }{%
    Inexistent~family~`#1'
  }
}
\prop_new:N \g_citr_standard_prop
\tl_new:N \l_citr_value_tl
\ExplSyntaxOff

\newcommand*{\setformulas}{
  \renewcommand*{\do}[1]{%
    % split the tuple <f/x/y>
    \StrBefore      {##1}{/}   [\f]
    \StrBetween[1,2]{##1}{/}{/}[\x]
    \StrBehind [2]  {##1}{/}   [\y]
    % set it
    \setproperty[\f]{X}{\x}
    \setproperty[\f]{Y}{\y}
    % this shows properties are correctly set, but...
    `\f::X' = \getproperty[\f]{X}\par
    `\f::Y' = \getproperty[\f]{Y}\par
  }
  \docsvlist{foo/xfoo/yfoo,bar/xbar/ybar}
}
\newcommand*{\dumpformulas}{
  \renewcommand*{\do}[1]{%
    `##1::X' = \getproperty[##1]{X}\par
    `##1::Y' = \getproperty[##1]{Y}\par
  }
  \docsvlist{foo,bar}
}

\begin{document}

Setting formulas:\par
\setformulas
Getting formulas:\par
\dumpformulas

\end{document}

我的问题是,似乎通过命令正确存储的属性\setformulas(其中,对的调用\getproperty用于调试目的)在稍后通过 (debug) 命令访问时似乎被破坏了\dumpformulas。 这是我得到的:

Setting formulas:
‘foo::X’ = xfoo
‘foo::Y’ = yfoo
‘bar::X’ = xbar
‘bar::Y’ = ybar
Getting formulas:
‘foo::X’ = xbar
‘foo::Y’ = ybar
‘bar::X’ = xbar
‘bar::Y’ = ybar

正如你所见,看起来“foo”prop 被“bar”prop 覆盖了,尽管日志表明实际上使用了两个不同的 props(或者至少这是我的解释):

\g_citr_foo_prop=\toks35
\g_citr_bar_prop=\toks36

相反,使用\set/getproperty外部\docsvlist循环效果很好。

我究竟做错了什么?

PS 我最初尝试使用pgffor \foreach循环,它可以提供更简洁的代码,尤其是避免xstring其中的诡计\setformulas,但却遇到了扩展问题......

答案1

我在这里注意到几件事,一是问题的关键,二是与expl3正在使用的版本有关。

这里最大的问题是

\prop_gput:cnn { g_citr_ #1 _prop } {#2} {#3}

里面\setproperty,并用它来

\setproperty[\f]{X}{\x}

LaTeX3 对于扩展非常谨慎,因此你存储\x不是属性列表中的值\x。例如,如果您使用,则可以看到这一点\prop_show:N \g_citr_bar_prop

The property list \g_citr_bar_prop contains the pairs (without outer braces):
>  {X}  =>  {\x }
>  {Y}  =>  {\y }.

当然,当你排版完材料后,你就会得到通过 \getproperty您看到的是当前数据的完整扩展,因为您刚刚使用了标记列表,而不是类似的东西\tl_to_str:N

您(可能)想要做的是传递变量的内容。现在,您混合了代码级和文档级语法,所以我不太确定第三个参数应该\setproperty是标记列表变量还是存储自身的材料。两种可能性之间的区别的演示:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox,xstring}

\ExplSyntaxOn

\NewDocumentCommand { \getproperty } { O { standard } m }
  { \citr_prop_get:nn {#1} {#2} }
\NewDocumentCommand { \setproperty } { O { standard } m m }
  { \citr_prop_set:nnn {#1} {#2} {#3} }

\tl_new:N \l_citr_value_tl

\cs_new_protected:Npn \citr_prop_set:nnn #1#2#3
  {
    \prop_if_exist:cF { g_citr_ #1 _prop }
      { \prop_new:c { g_citr_ #1 _prop } }
    \prop_gput:cnn { g_citr_#1_prop } {#2} {#3}
  }
\cs_generate_variant:Nn \citr_prop_set:nnn { nnV }
\cs_new_protected:Npn \citr_prop_get:nn #1#2
  {
    \prop_if_exist:cTF { g_citr_ #1 _prop }
      {
        \prop_get:cnNTF { g_citr_ #1 _prop } {#2} \l_citr_value_tl
% Convert the 'true' case to a string so we see what was stored
          { \tl_to_str:N \l_citr_value_tl }
          { Inexistent~property~`#2' }
      }
      { Inexistent~family~`#1' }
  }

\ExplSyntaxOff

\newcommand*{\setformulas}{%
  \renewcommand*{\do}[1]{%
    % split the tuple <f/x/y>
    \StrBefore      {##1}{/}   [\f]
    \StrBetween[1,2]{##1}{/}{/}[\x]
    \StrBehind [2]  {##1}{/}   [\y]
    % set it
    \setproperty[\f]{X}{\x}
    \setproperty[\f]{Y}{\y}
    % this shows properties are correctly set, but...
    `\f::X' = \getproperty[\f]{X}\par
    `\f::Y' = \getproperty[\f]{Y}\par
  }%
  \docsvlist{foo/xfoo/yfoo,bar/xbar/ybar}%
}
\newcommand*{\dumpformulas}{%
  \renewcommand*{\do}[1]{%
    `##1::X' = \getproperty[##1]{X}\par
    `##1::Y' = \getproperty[##1]{Y}\par
  }%
  \docsvlist{foo,bar}%
}

\begin{document}

Setting formulas:\par
\setformulas
Getting formulas:\par
\dumpformulas

\ExplSyntaxOn
\RenewDocumentCommand { \setproperty } { O { standard } m m }
  { \citr_prop_set:nnV {#1} {#2} #3 }
\ExplSyntaxOff

Setting formulas:\par
\setformulas
Getting formulas:\par
\dumpformulas

\end{document}

(我在这里只做了必要的更改以显示行为的差异。expl3如果我自己写的话,我可能会把所有内容都编码进去。)

在您的版本中expl3,至少几年来 toks 上都没有构建属性列表。因此,您在使用任何最近的 LaTeX3 示例时都会遇到严重问题:自我们做出这一更改以来,已经发生了许多其他更改。


expl3仅使用数据存储来实现整个过程的一种方法:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse,etoolbox}

\ExplSyntaxOn

\NewDocumentCommand { \getproperty } { O { standard } m }
  { \citr_prop_get:nn {#1} {#2} }
\NewDocumentCommand { \setproperty } { O { standard } m m }
  { \citr_prop_set:nnn {#1} {#2} {#3} }
\NewDocumentCommand { \setformulas } { } { \citr_formulas_set: }

\tl_new:N \l_citr_value_tl

\cs_new_protected:Npn \citr_prop_set:nnn #1#2#3
  {
    \prop_if_exist:cF { g_citr_ #1 _prop }
      { \prop_new:c { g_citr_ #1 _prop } }
    \prop_gput:cnn { g_citr_#1_prop } {#2} {#3}
  }
\cs_generate_variant:Nn \citr_prop_set:nnn { nnV }
\cs_new_protected:Npn \citr_prop_get:nn #1#2
  {
    \prop_if_exist:cTF { g_citr_ #1 _prop }
      {
        \prop_get:cnNTF { g_citr_ #1 _prop } {#2} \l_citr_value_tl
% Convert the 'true' case to a string so we see what was stored
          { \tl_to_str:N \l_citr_value_tl }
          { Inexistent~property~`#2' }
      }
      { Inexistent~family~`#1' }
  }

\cs_new_protected:Npn \citr_formulas_set:
  {
    \clist_map_inline:nn {  foo/xfoo/yfoo , bar/xbar/ybar }
      { \citr_split:nN {##1} \citr_formulas_set_aux:nnn }
  }
\cs_new_protected:Npn \citr_formulas_set_aux:nnn #1#2#3
  { 
    \citr_prop_set:nnn {#1} { X } {#2}
    \citr_prop_set:nnn {#1} { Y } {#3} 
  }

\cs_new:Npn \citr_split:nN #1#2 { \citr_split:wN  #1 \q_stop #2 }
\cs_new:Npn \citr_split:wN #1 / #2 / #3 \q_stop #4 { #4 {#1} {#2} {#3} }

\ExplSyntaxOff

\newcommand*{\dumpformulas}{%
  \renewcommand*{\do}[1]{%
    `##1::X' = \getproperty[##1]{X}\par
    `##1::Y' = \getproperty[##1]{Y}\par
  }%
  \docsvlist{foo,bar}%
}

\begin{document}

Setting formulas:\par
\setformulas
Getting formulas:\par
\dumpformulas

\end{document}

我在这里假设输入foo/xfoo/yfoo总是包含两个/标记,但问题并不清楚。如果不是,\seq_set_split:Nnn如果您不想按照传统的 TeX 方式做所有事情,那么类似这样的方法就很合适。另一种选择是l3regex

答案2

我不会混合expl3循环etoolbox和映射函数:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\setproperty}{O{standard}mm}
 {
  \prop_if_exist:cF { g_citr_#1_prop } { \prop_new:c { g_citr_#1_prop } }
  \prop_gput:cnn { g_citr_#1_prop } { #2 } { #3 }
 }
\NewDocumentCommand{\getproperty}{O{standard}m}
 {
  \cs_if_exist:cTF { g_citr_#1_prop }
   {
    \prop_get:cnNTF { g_citr_#1_prop } { #2 } \l_citr_value_tl
     { \tl_use:N \l_citr_value_tl }
     { Inexistent~property~`#2' }
   }
   {
    Inexistent~family~`#1'
   }
 }

\prop_new:N \g_citr_standard_prop
\tl_new:N \l_citr_value_tl

%%% Testing the macros above
\seq_new:N \l_citr_temp_seq
\NewDocumentCommand{\setformulas}{ }
 {
  \clist_map_inline:nn {foo/xfoo/yfoo,bar/xbar/ybar}
   {
    \seq_set_split:Nnn \l_citr_temp_seq { / } { ##1 }
    \setproperty[\seq_item:Nn \l_citr_temp_seq { 0 }]{X}{ \seq_item:Nn \l_citr_temp_seq { 1 } }
    \setproperty[\seq_item:Nn \l_citr_temp_seq { 0 }]{Y}{ \seq_item:Nn \l_citr_temp_seq { 2 } }
    `\seq_item:Nn \l_citr_temp_seq { 0 }::X' ~ = ~
       \getproperty[\seq_item:Nn \l_citr_temp_seq { 0 }]{X}\par
    `\seq_item:Nn \l_citr_temp_seq { 0 }::Y' ~ = ~
       \getproperty[\seq_item:Nn \l_citr_temp_seq { 0 }]{Y}\par
   }
}
\NewDocumentCommand{\dumpformulas}{ }
 {
  \clist_map_inline:nn {foo,bar}
   {
    `##1::X' ~ = ~ \getproperty[##1]{X}\par
    `##1::Y' ~ = ~ \getproperty[##1]{Y}\par
   }
}
\ExplSyntaxOff

\begin{document}

Setting formulas:\par
\setformulas
Getting formulas:\par
\dumpformulas

\end{document}

输出为

在此处输入图片描述

相关内容