Emacs 高效地从哈希表中检索按值排序的键

Emacs 高效地从哈希表中检索按值排序的键,emacs,lisp,elisp,Emacs,Lisp,Elisp,我正在使用Emacs Lisp,但是为一些常见的Lisp特性加载了cl包 我有一个哈希表,其中包含多达50K个条目,整数键映射到三元组,类似这样(但在实际的lisp中): 三元组中的第二个值是在构建哈希表的复杂算法期间计算的分数。我需要收集一个常规lisp列表,其中包含哈希中的所有键,按分数排序(即,所有键按值的cadr排序) 因此,对于上述内容,我需要以下列表: '(27 100 8) 我现在分两个阶段来做这件事,感觉效率比需要的要低 有什么好办法吗 我当前的解决方案使用maphhash将键

我正在使用Emacs Lisp,但是为一些常见的Lisp特性加载了
cl

我有一个哈希表,其中包含多达50K个条目,整数键映射到三元组,类似这样(但在实际的lisp中):

三元组中的第二个值是在构建哈希表的复杂算法期间计算的分数。我需要收集一个常规lisp列表,其中包含哈希中的所有键,按分数排序(即,所有键按值的cadr排序)

因此,对于上述内容,我需要以下列表:

'(27 100 8)
我现在分两个阶段来做这件事,感觉效率比需要的要低

有什么好办法吗

我当前的解决方案使用
maphhash
将键和值收集到两个新列表中,然后以正常方式对谓词中的分数列表进行
排序。不过,我觉得我可以将收集和分类结合在一起

EDIT |我也不喜欢使用哈希表,尽管我确实需要整数键的恒定访问时间,这些键不是线性间隔的

编辑2 |看起来可以实现二叉树排序,树中的标签是分数,值是键。。。这样,我在映射散列时进行排序


。。。继续阅读关于排序算法的维基百科页面基本上,您的解决方案是正确的:您需要将密钥收集到一个列表中:

(defun hash-table-keys (hash-table)
  (let ((keys ()))
    (maphash (lambda (k v) (push k keys)) hash-table)
    keys))
然后对列表进行排序:

(sort (hash-table-keys hash-table)
      (lambda (k1 k2)
        (< (second (gethash k1 hash-table))
           (second (gethash k2 hash-table)))))
(排序(哈希表键哈希表)
(λ(k1-k2)
(<(第二个(gethash k1哈希表))
(第二个(gethash k2哈希表‘‘‘‘)’)
将密钥收集与排序相结合是可能的:您需要将密钥收集到树中,然后“展平”树。然而,只有在处理非常大的表时,这才重要。此外,由于Emacs Lisp编译为字节码,您可能会发现使用内置的
排序
仍然比使用树更快。还要考虑开发成本——您需要编写代码,其代码将主要是教育。
更深入地研究,收集密钥会分配密钥列表(无论如何,结果都需要该列表),并且
sort
操作“到位”,因此“简单方法”几乎可以做到最好

“树”方式将分配树(与所需的键列表相同的内存占用),填充和展平树的过程与“收集+排序”方式相同。然而,保持树的平衡,然后将其“放平”(即,不分配新列表)并不是一个简单的练习


底线是:。

您是用Elisp还是用Common Lisp编程?我使用的是Elisp,但我确实加载了cl(Common Lisp)包,以实现一些(非常)基本的Common Lisp兼容性。请注意,
cl
不推荐使用;它给人一种CL兼容性的错觉,但也存在一些微妙的bug和不兼容性。当心!EmacsWiki有一个例子:这肯定比我之前做的要简单,而且现在我在代码中看到了这一点(显然我需要睡眠),虽然我确实想知道我是否可以避免最初收集键的50K操作,并将排序与这些键的集合相结合。感谢您在编辑中关于树排序的注释。我以前用lisp做过二叉树,只是从来没有为了排序。我将尝试一下,并将结果与仅使用sort进行比较。你是对的,在理论复杂性上的权衡可能会被一个用C语言完成,另一个用解释语言完成的权衡所抵消。即使在理论上,这也不是胜利。最多,您将赢得密钥收集的
n
操作,但与总
n*log(n)
成本相比,它相形见绌。您将花费数小时编写和调试代码,而不会获得性能提升。不过,这可能是一项有价值的教育活动。
(sort (hash-table-keys hash-table)
      (lambda (k1 k2)
        (< (second (gethash k1 hash-table))
           (second (gethash k2 hash-table)))))