【问题标题】:Implementing multidimensional associative arrays in Common Lisp在 Common Lisp 中实现多维关联数组
【发布时间】:2012-11-17 00:57:16
【问题描述】:

我正在寻找在 Common Lisp 中实现多维关联数组的最佳方法。我正在使用 SBCL。

例如在 MATLAB 中,我可以创建一个这样的结构数组,例如。带有一些地理数据:

my_array.Finland.capital = 'Helsinki';
my_array.Finland.northernmost_municipality = 'Utsjoki';
my_array.Norway.capital = 'Oslo';

然后使用动态字段名称引用它:

country = 'Finland';
city_status = 'capital';

my_array.(country).(city_status)

结果会给出字符串'Helsinki'

那么,在 Common Lisp 中实现多维(n 维)关联数组的最佳方法是什么?(如上面在 MATLAB 中的示例)

我找到了一个可行的解决方案,该解决方案基于使用哈希表将值/键(以字符串形式给出)转换为数字地址,然后将这些数字地址作为多维数组的索引传递给 aref。但我想知道有没有更好的方法来做到这一点?我需要将字符串转换为其他字符串,将字符串转换为列表,将字符串转换为数字(如下例所示)。

我的解决方案是这样的(用一个示例数据将用英文写的数字转换为它们的数字表示):

下面的函数list-to-3d-array 是Rainer Joswig 对Common Lisp: convert between lists and arrays 的回答的最小修改版本。

;;; functions.
(defun list-to-3d-array (my-list)
  (make-array (list (length my-list)
                    (length (first my-list))
                    (length (first (first my-list))))
              :initial-contents my-list))

(defun prepare-hash-table (my-hash-table factor-name factor-values-list)
  "This function stores values from 0 to n for the keys created by concatenating the factor-name and the values given in a list."
  (loop for i from 0 to (1- (length factor-values-list))
        do (setf (gethash (concatenate 'string factor-name "-" (nth i factor-values-list)) my-hash-table) i)))

(defun return-value-from-assoc-array (my-array my-hash-table hundreds tens ones)
  (aref my-array
        (gethash (concatenate 'string "hundreds-" hundreds) my-hash-table)
        (gethash (concatenate 'string "tens-" tens) my-hash-table)
        (gethash (concatenate 'string "ones-" ones) my-hash-table)))
;;; functions end here.

;;; some example data.
(defparameter *array-contents*
  (list 
    (list
      (list 111 112 113)
      (list 121 122 123)
      (list 131 132 133))
    (list
      (list 211 212 213)
      (list 221 222 223)
      (list 231 232 233))
    (list
      (list 311 312 313)
      (list 321 322 323)
      (list 331 332 333))))

(defparameter *hundreds-in-english* (list "hundred" "twohundred" "threehundred"))
(defparameter *tens-in-english* (list "ten" "twenty" "thirty"))
(defparameter *ones-in-english* (list "one" "two" "three"))

(defparameter *my-hash-table* (make-hash-table :test 'equal))
;;; example parameters end here.

;;; prepare the array.
(defparameter *my-array* (list-to-3d-array *array-contents*))

;;;; prepare the hash table.
(prepare-hash-table *my-hash-table* "hundreds" *hundreds-in-english*)
(prepare-hash-table *my-hash-table* "tens" *tens-in-english*)
(prepare-hash-table *my-hash-table* "ones" *ones-in-english*)

然后例如。 (return-value-from-assoc-array *my-array* *my-hash-table* "hundred" "ten" "two") 输出:

112

【问题讨论】:

    标签: multidimensional-array lisp hashtable common-lisp associative-array


    【解决方案1】:

    我不同意上面给出的get-nested-hash 的设计和实现。 (get-nested-hash 1) 不应返回 1,(get-nested-hash 1 2) 不应返回 nil。不传递两个参数应该是一个错误。应返回多个值以区分有意的 nil,例如 gethashloop 又乱又麻烦。不应通过check-type 发出可纠正的错误,因为可能会导致数据丢失。

    这是一个改进的解决方案:

    (defun rec-hash (rec-hash key &rest more-keys)
      (if more-keys
          (apply #'rec-hash (gethash key rec-hash) more-keys)
          (gethash key rec-hash)))
    
    (defun auto-vivify (rec-hash key)
      (multiple-value-bind (child exist) (gethash key rec-hash)
        (if exist
            child
            (setf (gethash key rec-hash)
                  (make-hash-table :test (hash-table-test rec-hash))))))
    
    (defun (setf rec-hash) (value rec-hash key &rest more-keys)
      (if more-keys
          (apply #'(setf rec-hash) value (auto-vivify rec-hash key) more-keys)
          (setf (gethash key rec-hash) value)))
    

    【讨论】:

    • 啊,这很好:) 我不知道有人可以#'(setf ..),太棒了!
    • 我是 Common Lisp 的新手。有人可以向我解释 #'(setf ...) 是什么意思吗?另外,我在这里在 ECL 上尝试过,它给了我一个错误。
    【解决方案2】:

    您还可以在 Lisp 中使用 Prolog 实现。 Prolog 提供了关系和断言事实的可能性,并且可以使用逻辑规则。

    这是 LispWorks 中的一个简单关系:

    ==> (defrel capital ((capital finland helsinki)))
    YES.
    OK.
    
    ==> (capital finland ?city)
    
    ?CITY = HELSINKI
    OK.
    
    ==> (and (= ?country finland) (capital ?country ?city))
    
    ?COUNTRY = FINLAND
    ?CITY = HELSINKI
    OK.
    

    通常,Lisp 中的此类 Prolog 还提供 Lisp 和嵌入式 Prolog 之间的访问。

    【讨论】:

      猜你喜欢
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 2014-09-03
      • 1970-01-01
      • 2017-06-08
      • 1970-01-01
      • 2020-12-18
      相关资源
      最近更新 更多