Lisp:使用语法糖访问递归散列

时间:2019-06-13 22:58:47

标签: lisp common-lisp

我正在尝试构建一个函数(或宏),以简化哈希表深处(即哈希内的哈希,哈希内的哈希等)中数据的获取和设置。我不认为我可以使用宏来实现,而且我不确定如何使用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)))

4 个答案:

答案 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)))

当然,这是有局限性的,不会创建不存在的哈希。