I am looking for a better way to implement multidimensional associative arrays in Common Lisp. I am using SBCL.
For example, in MATLAB I can create an array of structures like this, for example. with some geographic data:
my_array.Finland.capital = 'Helsinki'; my_array.Finland.northernmost_municipality = 'Utsjoki'; my_array.Norway.capital = 'Oslo';
And then access it using dynamic field names:
country = 'Finland'; city_status = 'capital'; my_array.(country).(city_status)
will result in the string 'Helsinki' .
So, what would be the best way to implement multidimensional (n-dimensional) associative arrays in Common Lisp? (as in the above example in MATLAB)
I found a working solution based on converting values ββ/ keys (given as strings) to numeric addresses using a hash table, and then passing those numeric addresses to aref as indices for a multidimensional array. But I wonder if there is a better way to do this? I need to convert strings to other strings, strings to lists, and strings to numbers (as in the example below).
My solution is this (with sample data for converting numbers written in English to their numeric representation):
The list-to-3d-array function below is a minimally modified version of Rainer Joswig on 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*)
Then, for example. (return-value-from-assoc-array *my-array* *my-hash-table* "hundred" "ten" "two") outputs:
112
source share