简体   繁体   中英

How would I make a matrix in elisp? ( and how does Emacs Cal do it?)

I'm interested in using elisp to practice implementing some linear algebra algorithms (speed isn't an objective here) - but elisp doesn't support multidimensional arrays like common lisp

I need to ultimately do things like get a values at an index, get a submatrix, get this column, get this row, etc. to write algorithms, decompositions and whatnot

How would I go about recreating something like that in elisp? ie. how do I go from simple data structures like lists and build up to a matrix?

I tried to look at Emacs calc

https://github.com/emacs-mirror/emacs/blob/65eee8392ff95f58f7b0bd036e1fe065523658c6/lisp/calc/calc-ext.el

but it's a little over my head and I don't see how the actual matrix is defined

(While I'm comfortable with the math, when it comes to lisps I only have a little scheme experience.. so maybe this is not really a good starter-project? haha)

The obvious approach in a language which has only one-dimensional arrays (or vectors) is to create vectors of vectors.

Here is a trivial implementation of this trick (very much not suitable for production use!) in elisp:

(defun make-simple-array (dims &optional init)
  ;; make n-dimensional array, represented as a vector of vectors (of
  ;; vectors ...).
  (if (null (cdr dims))
      (make-vector (car dims) init)
    (let* ((d1 (car dims))
           (dt (cdr dims))
           (v (make-vector d1 nil))
           (i 0))
      (while (< i d1)
        (aset v i (make-simple-array dt init))
        (setq i (1+ i)))
      v)))

(defun simple-array-ref (a indices)
  (if (null (cdr indices))
      (aref a (car indices))
    (simple-array-ref (aref a (car indices)) (cdr indices))))

(defun simple-array-set (a indices newval)
  (if (null (cdr indices))
      (aset a (car indices) newval)
    (simple-array-set (aref a (car indices)) (cdr indices) newval)))

This approach is simple but results in terrible locality: elements of an array can be scattered all over the place. A much better approach is to allocate one big vector and then compute the location of elements in it. That's how any serious array implementation would work.

For hack value, here is a primitive and partial implementation which keeps arrays as a big vector and computes the location. In this implementation arrays are stored as a cons of (v . factors) , where the factors are precomputed index factors you need to compute the index into v . This implementation has at least two problems:

  • arrays don't know their dimensions: you can compute this from the index factors and the length of the vector but I was too lazy to implement this;
  • dimensions are not checked, so if you have a 2x2 array, for instance, then you can access the element indexed by (0 2) , which is in fact the element indexed by (1 0) .

Anyway, here is an implementation.

(defun compute-flat-array-total-size (dimensions)
  ;; this is in fact (reduce #'* dimensions), but elisp
  (let ((s 1))
    (mapc (lambda (d) (setq s (* s d))) dimensions)
    s))

(defun compute-flat-array-index-factors (dimensions)
  (cond ((null dimensions)
         '(0))
        ((null (cdr dimensions))
         '(1))
        (t (let ((ftail (compute-flat-array-index-factors (cdr dimensions))))
             (cons (* (car dimensions) (car ftail))
                   ftail)))))

(defun compute-flat-array-index (indices factors)
  ;; Again, elisp sucks: you can't even use mapc here
  (let ((index 0)
        (itail indices)
        (ftail factors))
    (while (not (null itail))
      (when (null ftail)
        (error "too many indices"))
      (setq index (+ index (* (car itail) (car ftail)))
            itail (cdr itail)
            ftail (cdr ftail)))
    (unless (null ftail)
      (error "two few indices"))
    index))

(defun make-flat-array (dimensions &optional init)
  ;; a flat array is a cons of a vector of its contents and a list of
  ;; index factors.
  (cons (make-vector (compute-flat-array-total-size dimensions) init)
        (compute-flat-array-index-factors dimensions)))

(defun flat-array-ref (fa indices)
  (aref (car fa) (compute-flat-array-index indices (cdr fa))))

(defun flat-array-set (fa indices newval)
  (aset (car fa) (compute-flat-array-index indices (cdr fa)) newval))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM