我正在编写一个处理多语言术语公式的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}
输出为