expl3 中的属性列表的实现

expl3 中的属性列表的实现

对于某些预处理任务,我想使用expl3属性列表。由于该列表中的项目数量将达到数千个(教科书的索引条目),我想知道这样做的时间效率如何。因此,我做了一些测试:

\documentclass{article}

\usepackage{expl3,xparse, fp}

% timing adapted from http://tex.stackexchange.com/questions/9087
\def\startTimer{\pdfresettimer}
\def\stopTimer{%
    \FPdiv\result{\the\pdfelapsedtime}{65536}
    \FPround\result{\result}{3}\par 
    Time (seconds): \result \par}

\ExplSyntaxOn

\prop_new:N \sg_user_mapping
\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }

\int_new:N  \i_target
\int_new:N  \i_counter

\NewDocumentCommand{\dostuff}{m} {
    Adding\ #1\ keys\ to\ user\ mapping\par
    %\prop_gclear:N \sg_user_mapping
    \startTimer
    \int_add:Nn \i_target {#1}
    \int_zero:N \i_counter % Hopefully obvious!

    \int_while_do:nn { \i_counter < \i_target }
    {
        \edef\wurst{\int_to_arabic:n \i_counter}
        \prop_gput:Nxx \sg_user_mapping {hans\wurst} {0}
        \int_incr:N \i_counter
    }
    \stopTimer\par
}

\ExplSyntaxOff

\begin{document}

\ttfamily

\dostuff{10} 
\bigskip

\dostuff{100} 
\bigskip

\dostuff{1000} 
\bigskip

\dostuff{10000} 

\end{document}

以下是我得到的结果:

在此处输入图片描述

所以,这显然是 O^2 的情况;我猜测在插入新条目之前会扫描整个列表。

我想知道是否可以使用一些幕后技巧来利用pdftex内置的哈希表。例如,对于每个键值对,

  1. 通过删除任何不能成为命令名称一部分的字符来净化密钥:#foo%^bar!- baz?变成foobarbaz

  2. 创建名称为\pl_userland_foobarbaz

  3. 在该列表中存储#foo%^bar!- baz?=> 。whatever value

我确实尝试过,虽然代码在处理少量键时显然速度较慢,但​​确实可以按预期以 O^1 进行扩展,并且在约 1000 个键时达到与简单属性列表相同的水平。我犹豫是否要在此处发布该代码,因为我才刚刚开始expl3,专业人士可能会做得更好;这只是一个实验。

当然,我的建议提出了一个问题,在开始把玩具扔出婴儿车之前,pdfTeX 能够容纳多少这样的键和属性列表实例?或者,也许可以在 TeX 本身中实现一个适当的哈希表;但我在这方面还不够好。

答案1

首先,需要强调的是仅记录接口因为expl3并且那如果接口没有改变,实现可能会发生变化,恕不另行通知。(在我加入团队期间,我们在这里有过几种不同的实现,过去至少还有一种。)当前实现使用单个宏来保存整个属性列表(之前的实现使用单个 toks,在此之前存在一些 csname 奇怪之处:在我之前!)。

正如在如何在 TeX 中实现(低级)数组,在 TeX 中实现快速查找数组的最有效方法是使用哈希表,并确保控制序列名称不会引起太多冲突。(基本上,不要使用数字!)

然而,这并不是当前prop数据类型设计的关键设计驱动因素expl3。代码自在 LaTeX2e 发布之前(1994 年)当然还有另一个担忧:内存。如今,标准 TeX 二进制文件中可用的 csname 数量要多得多,因此保存它们的压力减少了(尽管根据使用情况,仍然有可能用完)。二十年前,压力要大得多:众所周知,在 MS-DOS 上使用 TeX 时,加载 LaTeX2e 内核后,只有大约 50 个“备用”名称。这个因素就是其中之一:随着时间的推移,实现方式已经发生了变化,但不只是简单地烧毁名称仍然是一个因素。

还有其他考虑因素。虽然使用哈希表方法进行随机查找很快,但在“幕后”复制属性列表更容易:它是一次性的,而不是循环。还有一个问题是如何映射列表中的每个条目:对于当前设置,我们可以扩展底层宏,然后处理堆栈上的条目。对每个条目使用单独的宏会使该过程更加棘手。

团队已经讨论过使用不同的实现,但到目前为止,从“用例”的角度来看,还没有强有力的推动力(当然对于团队自己的代码而言)。平衡各种用例并非易事,我们已经讨论过一种“对象”类型,它将具有多个“后端”,并可以在它们之间切换(也许通过用户设置)。

相关内容