对于令人费解的头衔,我尽力让自己有意识。好吧,如果你有更好的主意,请改变它!
不要混淆你,这是 Emacs Lisp loop
,而不是Common Lisp:
(defun hxswfml-build-trie (alist)
"Builds a trie (a list, containing number of hash-maps, each hash-map
uses single character for a key, except for `t' symbol, which, if present
as a key is the key for the value one has to substitute with."
(loop for (key . value) in alist
with trie = (make-hash-table)
do (loop for c across key
with branch =
(or (gethash c trie)
(puthash c (make-hash-table) trie))
with first-time = t
do (if first-time (setq first-time nil)
(setq branch
(or (gethash c branch)
(puthash c (make-hash-table) branch))))
finally (puthash t value branch))
finally (return trie)))
这会将alist转换为由哈希表组成的树,其中每个表都包含键,这些键是我稍后搜索和替换的字符串的字符。这需要优化搜索多个键,可能在大文本中使用相似的前缀,然后用相应的键替换它们。
问题在于,在内部循环中,我想要将branch
初始化为trie
,然后在所有后续迭代中将其设置为新的散列表(为字符创建而不是已知前缀的一部分),或者已经为前缀中的字符创建的哈希表。
理想情况下,它看起来像:
for branch = (or (and branch (gethash c branch)) (puthash c (make-hash-table) trie))
;; ^-----------------^------- cannot reference it here
这就是为什么我有一个愚蠢的first-time
旗帜,我可以避免。我可以以某种方式使用initially
形式,或者以某种其他方式重构函数以避免此标志和额外的if
吗?这个函数快速并不是很重要(搜索应该很快,但树的构建不需要),但它看起来很丑陋:))
答案 0 :(得分:3)
由于您明确提到重构是一个潜在的选项,我建议将您的函数组合的两个操作分开:创建trie并将元素插入到trie中。
如果您将try的定义视为更模块化的数据结构,您可以从以下两个函数开始:
(defun trie-create ()
(make-hash-table :test 'equal))
(defun trie-put (key value trie)
(if (equal key "")
(puthash t value trie)
(let* ((c (substring key 0 1))
(child-trie (gethash c trie)))
(unless child-trie
(setq child-trie (trie-create))
(puthash c child-trie trie))
(trie-put (substring key 1) value child-trie))))
(正如你所看到的,我建议在这里递归而不是嵌套的loop
- 这可能是一个品味问题,但在我看来,这使得代码更简单,更清晰。)< / p>
接下来,您可能希望添加trie-get
或trie-remove
等功能。
使用此代码,将alist转换为trie成为创建新trie然后使用上述函数将所有元素插入其中的组合:
(let ((trie (trie-create)))
(mapc '(lambda (x) (trie-put (car x) (cdr x) trie)) alist))
答案 1 :(得分:2)
未测试:
(defun hxswfml-build-trie (alist)
"Builds a trie (a list, containing number of hash-maps, each hash-map
uses single character for a key, except for `t' symbol, which, if present
as a key is the key for the value one has to substitute with."
(loop for (key . value) in alist
with trie = (make-hash-table)
for leaf = (reduce (lambda (branch c)
(or (gethash c branch)
(puthash c (make-hash-table) branch)))
key :initial-value trie)
do (puthash t value leaf)
finally (return trie)))
答案 2 :(得分:2)
请注意,已经有一个trie.el
包实现了Elisp中的常规尝试(免责声明:我是包作者)。它已经存在了几年了,最近Emacsen可以从GNU ELPA获得。或者可以从the package's web page下载。
默认情况下,它使用AVL树作为尝试的基础数据结构,而不是哈希表。但是,您可以在创建trie时指定不同的基础数据结构。所有标准的trie搜索(加上一些额外的)都被实现,并且与底层数据结构无关。
这不会直接回答您的问题,但可能会为您节省工作。
答案 3 :(得分:1)
我不确定我理解它,但在Common Lisp中我会这样做:
(loop for i = (foo) then (1+ i) ...)