3

*my-array*次のような配列があるとします。これを呼び出します。

#2A((1 2 3)
    (4 5 6)
    (7 8 9))

サブアレイに関数 f を適用したい

#2A((5 6)
    (8 9))

書いていただけると幸いです

(f (subarray *my-array* '(1 2) '(1 2))

wheresubarrayは引数として取ります:

  • 元の配列
  • 1次元に始点と終点を持つ2要素リスト
  • 2 番目の次元に開始点と終了点がある別の 2 要素リスト

valueではなく、f 参照によって(またはポインターによって)サブ配列を引数として関数に渡す方法を探しています。

(これに対処する愚かな方法は、(この特定のケースでは) 2*2 配列を作成し、元の配列から値をコピーする i と j をループ処理する関数を作成することです。しかし、比較的大きな配列を扱っている場合、これはかなりの費用がかかります。)

パッケージが存在することがわかりましたが、cl-slice値をコピーするのか、参照によってデータにアクセスするのかわかりません。

4

3 に答える 3

6

Common Lisp にはDisplaced Arraysがありますが、これはまさにあなたが求めているものです ( array-displacement&c を参照)。

ただし、あなたの場合、次の理由により、変位配列は役に立ちませ

多次元配列は、コンポーネントを行優先順に格納します。つまり、内部的に多次元配列は 1 次元配列として格納され、多次元インデックス セットは辞書式に並べられ、最後のインデックスが最も速く変化します。

これは、サブ配列がメイン配列の連続したセクションではないことを意味し、したがって、それに移動する別の配列を作成することはできません。

PS。cl-sliceがどのように機能するかがわからない場合は、使用timeしてメモリの使用量を確認し、そこから推測することができます。

PPS。実際、あなたが望むようなものを作るのはそれほど難しいことではありません:

(defmacro slice (array &rest ranges)
  "Return an accessor into ARRAY randing in RANGES."
  (let ((args (loop for r in ranges collect (gensym "SLICE-ARG-")))
        (arr (gensym "SLICE-ARRAY-")))
    `(let ((,arr ,array))
       (lambda ,args
         (aref ,arr
               ,@(loop for arg in args and (lo hi) in ranges
                   for range = (- hi lo)
                   collect
                     `(progn
                        (unless (<= 0 ,arg ,range)
                          (error "~S is out of range [0;~S]" ,arg ,range))
                        (+ ,lo ,arg))))))))

(defparameter *my-array*
  #2A((1 2 3)
      (4 5 6)
      (7 8 9)))

(defparameter f (slice *my-array* (1 2) (1 2)))

(loop for i from 0 to 1 do
    (loop for j from 0 to 1 do
        (format t " ~S" (funcall f i j)))
    (terpri))
 5 6
 8 9
于 2015-08-18T16:00:51.380 に答える
4

他の人が指摘したように、行列に変位配列を使用することはできません (おそらく、非標準関数を使用できます)。ただし、必要なのは、元の配列との対話方法を変更することだけです。ここにいくつかの可能性があります。

変位配列のシーケンス

(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 つの引数はxy内側の行列に対する座標です。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
于 2015-08-18T16:16:44.100 に答える
3

あなたがやりたいことを正確に行うことは不可能だと思います。メモリ内では、多次元配列は、多次元インターフェイスからフラット インターフェイスへの変換に使用されるいくつかのメタデータを含む単一のフラット配列として実装されます。あなたの場合、次の*my-array*ようになります。

#(1 2 3 4 5 6 7 8 9)

元の配列への参照として必要なサブ配列がある場合、次のようになります。

#(5 6 _ 8 9)

7元の配列をスキップしようとしているため、これは不可能です。:displaced-to必要なすべての要素が連続したサブシーケンスの一部である場合、参照によってサブシーケンスをコピーするために の引数を使用できますがmake-array、残念ながらそうではありません。

于 2015-08-18T16:01:15.950 に答える