我正在尝试构建一个函数(或宏),以简化哈希表深处(即哈希内的哈希,哈希内的哈希等)中数据的获取和设置。我不认为我可以使用宏来实现,而且我不确定如何使用eval来实现。我希望能够执行以下操作:
(gethashdeep *HEROES* "Avengers" "Retired" "Tony Stark")
并返回“钢铁侠”
所有哈希都是用以下方式创建的:
(setf hashtablename (make-hash-table :test 'equal))
并从那里填充。
我可以执行以下操作,但是想要对其进行抽象,以便可以以编程方式从任意深度提取值:
;;pulling from a hash that's 2 deep
(gethash "Tony Stark" (gethash "Avengers" *HEROES*))
更新-我要去做:
(defun getdeephash (hashpath h k)
(let* ((rhashpath (reverse hashpath))
(hashdepth (list-length hashpath))
(hashcommand (concatenate 'string "(gethash \"" k "\"")))
(loop for i from 1 to hashdepth
do (setf hashcommand (concatenate 'string hashcommand "(gethash \"" (nth (- i 1) rhashpath) "\"")))
(setf hashcommand (concatenate 'string hashcommand " " h (make-string (- hashdepth 0) :initial-element #\Right_Parenthesis) ")"))
(values hashcommand)))
答案 0 :(得分:8)
嵌套表查找不需要花哨的东西。
gethash-deep
的一个可能定义:
(defun gethash-deep (value &rest keys)
(if (or (endp keys)
(not (hash-table-p value)))
value
(apply #'gethash-deep
(gethash (first keys) value)
(rest keys))))
示例用法:
(defun table (&rest keys-and-values &key &allow-other-keys)
(let ((table (make-hash-table :test 'equal)))
(loop for (key value) on keys-and-values by #'cddr
do (setf (gethash key table) value))
table))
(defparameter *heroes*
(table "Avengers"
(table "Retired" (table "Tony Stark" "Iron Man")
"Active" (table "Bruce Banner" "Hulk"))))
(gethash-deep *heroes* "Avengers" "Retired" "Tony Stark") => "Iron Man"
(gethash-deep *heroes* "Avengers" "Active" "Bruce Banner") => "Hulk"
答案 1 :(得分:8)
它是Access库的一个衬里:
(ql:quickload "access")
我们定义*heroes*
哈希表(如Xach的示例):
(defun table (&rest keys-and-values &key &allow-other-keys)
(let ((table (make-hash-table :test 'equal)))
(loop for (key value) on keys-and-values by #'cddr
do (setf (gethash key table) value))
table))
TABLE
(defparameter *heroes*
(table "Avengers"
(table "Retired" (table "Tony Stark" "Iron Man")
"Active" (table "Bruce Banner" "Hulk"))))
通常,我们使用access:access
来一致地访问各种数据结构(alist,plist,hash表,对象等)。对于嵌套访问,我们使用access:accesses
(复数):
(access:accesses *heroes* "Avengers" "Retired" "Tony Stark")
"Iron Man"
此外,我们可以setf
:
(setf (access:accesses *heroes* "Avengers" "Retired" "Tony Stark") "me")
"me"
这是一个经过战斗验证的库,因为它是Djula模板库(下载次数最多的Quicklisp库之一)的核心。
我的博客文章:https://lisp-journey.gitlab.io/blog/generice-consistent-access-of-data-structures-dotted-path/
答案 2 :(得分:4)
对于临时结构,可以使用->>
中的arrows
:
(->> table
(gethash "Avengers")
(gethash "Retired")
(gethash "Tony Stark"))
如果您想与其他访问器(例如aref
)混合使用,则可以使用as->
或-<>
来处理不同的参数顺序。
如果我想实现它,我将依靠隐式检查,并可能使用reduce
:
(defun gethash-deep (hash-table &rest keys)
(reduce (lambda (table key)
(gethash key table))
keys
:initial-value hash-table))
或loop
:
(defun gethash-deep (table &rest keys)
(loop :for k :in keys
:for v := (gethash k table) :then (gethash k v)
:finally (return v)))
答案 3 :(得分:0)
如果所有键都存在,这应该可以工作,gethash
不能直接用于化简的唯一原因是它接收到的输入顺序不正确,可以正常工作。
(defun gethashdeep (table &rest keys)
(reduce #'(lambda (h k)
(gethash k h)) (cdr keys)
:initial-value (gethash (car keys) table)))
当然可以将其写为宏并且可以设置
(defmacro gethashdeep1 (table &rest keys)
(reduce #'(lambda (h k) (list 'gethash k h)) (cdr keys)
:initial-value (list 'gethash (car keys) table)))
当然,这是有局限性的,不会创建不存在的哈希。