我如何在elisp中制作矩阵? (以及Emacs Cal如何做到这一点?)

时间:2017-05-14 16:05:48

标签: list emacs lisp

我有兴趣使用elisp来练习实现一些线性代数算法(速度不是这里的一个目标) - 但是elisp并不支持像常见的lisp这样的多维数组

我需要最终做一些事情,比如在索引处获取值,获取子矩阵,获取此列,获取此行等,以编写算法,分解等等

我如何在elisp中重新创建类似的东西? 即。我如何从简单的数据结构,如列表和构建到矩阵?

我试着看看Emacs calc

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

但它有点过头了,我不知道如何定义实际矩阵

(虽然我对数学很满意,但对于lisps我只有一点方案经验..所以也许这不是一个好的入门项目?哈哈)

1 个答案:

答案 0 :(得分:0)

在只有一维数组(或向量)的语言中,显而易见的方法是创建向量的向量。

这是一个简单的实现这个技巧(非常不适合生产使用!)在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)))

这种方法很简单,但会导致可怕的局部性:数组的元素可能遍布整个地方。更好的方法是分配一个大向量,然后计算其中元素的位置。这就是任何严肃的阵列实现都会起作用的。

对于hack值,这里是一个原始的部分实现,它将数组保持为一个大向量并计算位置。在此实现中,数组存储为(v . factors)的缺点,其中factors是计算索引到v所需的预先计算的索引因子。这个实现至少有两个问题:

  • 数组不知道它们的维度:你可以从索引因子和向量的长度来计算它,但我实在太懒了;
  • 维度未被检查,因此如果你有一个2x2数组,那么你可以访问由(0 2)索引的元素,这实际上是由(1 0)索引的元素。

无论如何,这是一个实现。

(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))