Question

I'm trying to create a multidimensional array with initial content that is determined by a function. It is easy to do for any specified dimensionality of the array.

For example for 2x2 array:

(defmacro array-generation (fun &rest size-dimensions)
 (let ((a (gensym)) 
       (b (gensym))) 
    `(make-array ',size-dimensions
       :initial-contents 
          (loop for ,a from 0 below (first ',size-dimensions) by 1           
       collect 
          (loop for ,b from 0 below (second ',size-dimensions) by 1
       collect (,fun  ,a ,b))))))

(defparameter bla (array-generation + 2 3))

Gives me #2A((0 1 2) (1 2 3)).

How do I generalize the macro for any dimensionality? For example for 2x3x5x6x7x8

(defparameter bla (array-generation + 2 3 5 6 7 8))
Was it helpful?

Solution

(defmacro array-generation (fun &rest dims)
  (let ((syms (loop :repeat (length dims) :collect (gensym))))
    (reduce (lambda (x y) (append x (list y)))
            (mapcar (lambda (sym dim)
                      `(loop for ,sym from 0 below ,dim by 1 collect))
                    syms dims)
            :initial-value (cons fun syms)
            :from-end t)))

OTHER TIPS

You already have an answer, so I would just like to add this as food for thought. Three observations:

  • It's not necessary for array-generation to be a macro; it can be a function.
  • It may be easier to initialise the array explicitly instead of generating a list for consumption by :initial-contents.
  • The default initial value for from is 0, so it can be left out.
(defun array-generation (fn &rest dimensions)
  (let ((array (make-array dimensions)))
    (labels ((init (dims indices)
               (if (null dims)
                   (setf (apply #'aref array indices) (apply fn indices))
                   (loop for i below (first dims) do
                         (init (rest dims) (cons i indices))))))
      (init (reverse dimensions) nil)
      array)))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top