他の人が指摘したように、行列に変位配列を使用することはできません (おそらく、非標準関数を使用できます)。ただし、必要なのは、元の配列との対話方法を変更することだけです。ここにいくつかの可能性があります。
変位配列のシーケンス
(defun area (matrix tlx tly brx bry)
;; you may also want to check that all coordinates are valid
;; inside current matrix. You could generalize this function for
;; more dimensions.
(assert (<= tlx tly))
(assert (<= brx bry))
(loop
for y from tly upto bry
collect (make-array (1+ (- brx tlx))
:displaced-to matrix
:displaced-index-offset
(array-row-major-index matrix y tlx))))
(tl
左上、br
右下を意味します)。
次に、マトリックスを次のように定義するとします。
(defparameter *matrix* #2A((1 2 3)
(4 5 6)
(7 8 9)))
... サブマトリックスは次のように取得されます。
(area *matrix* 1 1 2 2)
=> (#(5 6) #(8 9))
...そして次のようにアクセスします:
(aref (nth ROW *) COL)
への変更*matrix*
は、2 つの変位配列のいずれかに反映され、逆に反映されます。
しかし、結果のリストを として強制するとvector
、配列のベクトルが得られます。これは多次元配列とは異なりますが、行に一定時間アクセスできます。
(aref (aref area ROW) COL)
ラッパークロージャー
元のマトリックスの制限されたビューを提供する別の方法は、関心のある範囲に対してのみ機能するアクセサー関数を作成することです。
(defun sub-matrix (matrix tlx tly brx bry)
;; again, you should do more checks
(assert (<= tlx tly))
(assert (<= brx bry))
(lambda (x y &optional (value nil valuep))
(incf x tlx)
(incf y tly)
(assert (<= tlx x brx))
(assert (<= tly y bry))
(if valuep
(setf (aref matrix y x) value)
(aref matrix y x))))
これは、2 つまたは 3 つの引数を取るクロージャを返します。最初の 2 つの引数はx
、y
内側の行列に対する座標です。3 番目の引数を指定すると、クロージャーは値を設定します。それ以外の場合は、値を取得します。
これは、より一般的なものにすることができます。私はsdsの答えに部分的に触発されましたが、少し違うことをしようとしました。ここで、セッターまたはゲッター関数のいずれかを生成できます。また、関数を作成する前と、作成した関数の実行中にいくつかのチェックを追加します。
(defun slice-accessor (array ranges mode)
(let* ((dimensions (array-dimensions array))
(max-length (length dimensions)))
(check-type array array)
(loop
with r = (copy-list ranges)
for range = (pop r)
for (lo hi) = range
for d in dimensions
for x from 0
for $index = (gensym x)
collect $index into $indices
when range
do (assert (<= 0 lo hi d))
and collect `(check-type ,$index (integer 0 ,(- hi lo))) into checks
and collect `(incf ,$index ,lo) into increments
finally (let ((body `(apply #'aref ,array ,@$indices ())))
(return
(compile nil
(ecase mode
(:read `(lambda ,$indices
,@checks
,@increments
,body))
(:write (let (($v (make-symbol "VALUE")))
`(lambda (,$v ,@$indices)
(check-type ,$v ,(array-element-type array))
,@checks
,@increments
(setf ,body ,$v)))))))))))
閉鎖
上記を取得したら、オブジェクトを介して優れたインターフェイスを提供できます。セッター関数とゲッター関数は、範囲またはスライスされた配列を変更するたびに更新されます。
(defclass array-slice ()
((array :initarg :array :accessor reference-array)
(ranges :initarg :ranges :accessor slice-ranges :initform nil)
(%fast-getter :accessor %fast-getter)
(%fast-setter :accessor %fast-setter)))
(flet ((update-fast-calls (o)
(setf (%fast-setter o)
(slice-accessor (reference-array o) (slice-ranges o) :write)
(%fast-getter o)
(slice-accessor (reference-array o) (slice-ranges o) :read))))
(defmethod initialize-instance :after ((o array-slice) &rest k)
(declare (ignore k))
(update-fast-calls o))
(defmethod (setf reference-array) :after (new-array (o array-slice))
(declare (ignore new-array))
(update-fast-calls o))
(defmethod (setf slice-ranges) :after (new-ranges (o array-slice))
(declare (ignore new-ranges))
(update-fast-calls o)))
(defgeneric slice-aref (slice &rest indices)
(:method ((o array-slice) &rest indices)
(apply (%fast-getter o) indices)))
(defgeneric (setf slice-aref) (new-value slice &rest indices)
(:method (new-value (o array-slice) &rest indices)
(apply (%fast-setter o) new-value indices)))
例
(defparameter *slice*
(make-instance 'array-slice :array *matrix*))
;; no range by default
(slice-aref *slice* 0 0)
=> 1
;; update ranges
(setf (slice-ranges *slice*) '((1 2) (1 2)))
(slice-aref *slice* 0 0)
=> 5
(incf (slice-aref *slice* 0 0) 10)
=> 15
*matrix*
=> #2A((1 2 3) (4 15 6) (7 8 9))
;; change array
(setf (reference-array *slice*) (make-array '(3 3) :initial-element -1))
(slice-aref *slice* 0 0)
=> -1