Search code examples
arrayscommon-lisp

Looking for a way to map a function over a 2D Lisp array


All of the map functions like mapcar, mapcan, mapc, etc require lists as their inputs. I am working with 2D arrays, and would prefer to not mess with the rank of my arrays given the often large sizes (50,000 x 1,000 sometimes).

I need a way to apply a function like (log n) to each element in the 2D array and yield a resulting 2D array.

Any help or direction is much appreciated.

Working in AllegroCL (Common Lisp)


Solution

  • What you need is a combination of

    Something like this:

    (defun array-map (function array
                      &optional (retval (make-array (array-dimensions array))))
      "Apply FUNCTION to each element of ARRAY.
    Return a new array, or write into the optional 3rd argument."
      (dotimes (i (array-total-size array) retval)
        (setf (row-major-aref retval i)
              (funcall function (row-major-aref array i)))))
    

    Example:

    (defparameter a (make-array '(2 3) :initial-contents '((1 2 3) (4 5 6))))
    a
    ==> #2A((1 2 3) (4 5 6))
    (array-map #'sqrt a)
    ==> #2A((1 1.4142135 1.7320508) (2 2.236068 2.4494898))
    a ; does not change!
    ==> #2A((1 2 3) (4 5 6))
    

    You can also use array-map similar to map-into:

    (array-map #'1+ a a)
    ==> #2A((2 3 4) (5 6 7))
    a   ; modified, no new storage is allocated
    ==> #2A((2 3 4) (5 6 7))
    

    Note that array-map will work on any array dimension, from vectors to matrices to 10d &c.

    Exercise: implement array-multi-map accepting a function of any number of argument and any number of arrays, so that

    (array-multi-map #'+ #A((1 2 3) (4 5 6)) #A((11 22 33) (44 55 66)))
    ==> #A((12 24 36) (48 60 72))
    

    PS. The whole CLHS Chapter 15 Arrays or CLtL2 Chapter 17 Arrays is worth studying.