(rontolisp) docs
← cl パッケージの関数

make-array

(make-array dimensions &key initial-element initial-contents element-type fill-pointer adjustable displaced-to displaced-index-offset)

新しい配列を作成して返します。dimensions はランク 1 のベクタの場合は整数、任意のランクの配列の場合は整数のリストです。空のリスト (nil) も指定でき、その場合はランク 0 の配列になります。これは Common Lisp における「スカラーを配列として見た箱」であり、要素を 1 つだけ保持し、aref は添字なしでその要素を読み書きします。印字形式は #0A<datum> で、リーダはこれをそのまま読み戻せます (#0A5、#0A(1 2) はリストを保持するランク 0 の配列)。:initial-element はすべてのセルを指定した値に設定します。デフォルトは nil です。要素は行優先で格納され、aref を介して O(1) でアクセスできます。配列は同一性 (eq) で比較されるため、異なる 2 つの配列が equal になることはありません。make-array と aref は第一級の関数値ではありません。#'make-array は利用できないため、直接呼び出してください。

各次元は array-dimension-limit 未満の整数、その積は array-total-size-limit 未満でなければならず、割り当ての前に検査されます。そうでない次元 (bignum、負の数、整数以外) は、その次元を datum、(INTEGER 0 (limit)) を期待型とする type-error を通知します。積が上限を超える場合は、上限に達した最初の部分積を datum として同じ type-error を通知し、ドット対で終わる次元リストの末尾は LIST 型ではないという type-error になります。上限はバックエンドごとの値で、インタプリタと JVM では 2147483639、WASM では 1073741823 です。

:fill-pointer (ランク 1 のみ) はベクタにフィルポインタを与えます。整数はその位置に、t はベクタサイズに設定します。フィルポインタは実効長であり、length や印字はフィルポインタで止まります (aref はストレージ全体にアクセスできます)。vector-push/vector-pop/vector-push-extend が操作するのもこのフィルポインタです。:adjustable は配列を可変長としてマークし、adjustable-array-p がそのまま報告します。可変長配列は adjust-array でその場でリサイズされます。:initial-contents は (入れ子の場合もある) シーケンスから配列を充填します (row-major。すべてのバックエンドで、任意のランクに対応)。各階層の長さは対応する次元と一致する必要があり、異なる場合はその軸を示すエラーを通知します。:element-type 'double-float/'single-float (フィルポインタ/可変長/displacement なし) はパックド浮動小数点表現を選択し、同じ条件の :element-type 'character は文字列を作ります (ランク 1 の文字配列は文字列そのものであり、make-string の結果と同じ形です)。:fill-pointer/:adjustable 付きの :element-type 'character は、すべてのバックエンドでフィルポインタ付きの可変文字列を作ります: vector-push-extend で文字を追加でき、replace と (setf (char ...)) はその場に書き込み、文字列として印字・比較 (string=/equal、equal ハッシュ表のキー) され、stringp を満たします。:initial-contents 付きの :element-type 'character は内容 (文字列、可変文字列、または文字のリスト) を新しい単純文字列にコピーします。これら 3 つの文字用の形はいずれもランク 1 限定です。文字列はランク 1 の文字配列であり、それ以外ではありえないからです: ランク 2 以上の dimensions に :element-type 'character を与えると通常の一般配列になり (stringp と vectorp は nil)、その未指定要素は依然として文字であり #\Space が既定値になります。専用の表現を選ばない要素型も記憶されます。ランク 2 以上、あるいは :fill-pointer/:adjustable との組み合わせでは配列は一般表現になりますが、array-element-type は character / (unsigned-byte n) / single-float / double-float を返し、type-of はそこから複合指定子を組み立て、値を与えられなかった要素は nil ではなくその型のゼロ (#\Space、0、0.0) になります。ランク 1 の配列に対する:element-type '(unsigned-byte 8)、'(unsigned-byte 16)、'(unsigned-byte 32) (同じくフィルポインタ/可変長/displacement なし) はパックド符号なし整数ベクタを選択します: 格納は値を要素幅にマスクし (2 の補数での切り詰め)、読み出しは符号なしに拡大して返し、整数以外の格納はエラーになります。array-element-type は実際の (unsigned-byte n) 指定子を報告します。パックドベクタの subseq と copy-seq は同じ幅のパックドベクタのままです。引数なしの deftype 名はこれらの判定より先に解決されるため、別名を書いても展開先を書いたときとまったく同じ表現が選ばれます。:element-type はリテラルで書く必要はありません。実行時に計算された指定子 (変数、(stream-element-type s) の呼び出し) は、その値をリテラルで書いた場合とまったく同じものを、すべてのバックエンドで選択します。変数に入った deftype の別名も同様です。それ以外の要素型 (fixnum、integer、クラス) は受け付けられますが、アップグレード先の表現がないため配列は一般表現になり、array-element-type は t を返します。:element-type 'bit だけは例外です。ビットベクタとは bit で刻印された汎用ボックス配列であり (パックされたビット記憶はありません)、array-element-type は bit を返し、bit-vector-p がそれを認識し、値を与えられなかった要素は 0 になります。型ではない名前 (打ち間違い、プログラムが登録していない deftype 名) も同じ経路をたどります: make-array は要素型についてエラーを通知せず t にアップグレードします。任意の要素型をアップグレードしてよいという Common Lisp の規則どおりの動作です。したがって、パックド表現が得られることに依存するコードは、割り当てた後に array-element-type を問い合わせて、その事後条件を自分で確認してください。

:displaced-to は、ストレージを割り当てる代わりに別の配列のストレージへのビューを構築します。ビューの (行優先の) 要素 i はターゲットの要素 i + offset を読み書きします (:displaced-index-offset のデフォルトは 0)。変更は双方向に見えます。ビューは独自の次元を持ち (ターゲットとランクが異なってもよく、例えば行列の行に対するベクタビューが作れます)、ターゲット内に収まる必要があり、array-displacement で調べられます。:displaced-to と併用できないのは :initial-element と :initial-contents だけです (ビューは初期化すべきストレージを持たないため)。:fill-pointer と :adjustable は併用でき、いずれもビュー自身のものです: フィルポインタはビューの実効長であり (length と印字はそこで止まり、aref はビューの次元全体に届きます)、vector-push/vector-pop はターゲットへ書き抜けます。ビューが満杯になって伸長が必要になると、vector-push-extend はビューの displacement を解除します: 現在の内容が自前のストレージへ移り、以降 array-displacement は nil を返し、伸長がターゲットに触れることはありません。displaced ビュー自体も adjust-array で adjust できます: 満杯のビューが vector-push-extend で伸長するときと同様、まず displacement を解除してから調整されます。

パックドなターゲット ((unsigned-byte 8|16|32) ベクタ、または double-float/single-float 配列) に対する displace も同じ条件で動きます。これは :displaced-to の要素型規則が本来想定している形です。ビューはターゲットのアンボックスなストレージを直接読み書きするため、ビュー経由の格納はターゲットへ直接格納したときとまったく同じように要素幅へマスク/丸められ、array-element-type は t ではなくターゲットの実際の型を返します。ビュー自体はパックドベクタではなく通常の配列ビューです: フィルポインタを持つこともでき、displacement が解除される (満杯での vector-push-extend、または adjust-array) と内容はその要素型を記憶したままの一般ストレージへ移るので、伸長で開いたスロットにはその型のゼロが入ります。

文字列に対して displace すると文字列ビューになります。形を決めるのは要素型ではなくターゲットなので、結果は stringp を満たし、オフセット以降のターゲットの文字を持ち、文字列として印字・比較され、subseq/char/length はその範囲を見ます。コピーは発生しません。移植性のあるライブラリが部分文字列を共有するのはこの形です。ビュー経由の書き込みはターゲットに書き込まれます (ビューのビューも同じ文字に届きます)。実行中のプログラムが割り当てた文字列 (make-string バッファ、copy-seq/subseq の切り出し、concatenate 'string / string-upcase / format nil / with-output-to-string / read-line の結果) はすべてのバックエンドで可変であり、そのビュー経由の書き込みはターゲットに届きます。コンパイル済みバックエンドでは、文字列リテラル (および princ-to-string のような、そこでまだ不変値を返す少数のプロデューサの結果) は書き込みの届かない不変値です。ビューはそれをコピーせずに読み、ビュー経由の最初の書き込みでビューは可変コピーに移り、元の文字列はそのまま残ります。これはその文字列に対する (setf (char s i) c) の挙動とまったく同じです。