4

我正在寻找在 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 的回答的最小修改版本:在列表和数组之间转换

;;; 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
4

2 回答 2

6

我不同意get-nested-hash上面给出的设计和实现。(get-nested-hash 1)不应返回 1,(get-nested-hash 1 2)也不应返回 nil。不传递两个参数应该是一个错误。应返回多个值以区分有意的 nil,例如gethash. 这loop是凌乱和繁琐的。不应发出可纠正的错误,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)))
于 2012-12-01T20:35:50.140 回答
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 之间的访问。

于 2012-11-29T20:25:01.027 回答