配列の値
配列とは可変長のインデックス可能な値のシーケンスです。
配列の値にはすべての配列タイプを使用できます。
配列の値はマップ可能です。
リテラル
#(<value>, <value>, ...)
#() -- an empty array
配列リテラルも参照してください。
コンストラクタ:
コレクションを配列に変換します。
プロパティ
配列中の要素の数を取得/設定します。リリース 5 より前の 3ds Max のバージョンでは、このプロパティは読み込み専用でした。
演算子
配列の要素に値を設定します。インデックスは 1 から始まります。
<array>[<integer>] = <value>
配列の要素に値を設定します。必要に応じて配列を拡張します。
まったく新しい配列を含まない join のように、最初と 2 番目のオペランドの要素をすべて含むものが構成されます。
例:
|
a = #(1,2,3,4)
join a #(5,6,7,8)
(cameras as array) + lights
props = getPropNames Node
join props (getPropNames(classOf Box))
join props (getPropNames Box #dynamicOnly)
sort props
|
メソッド
値を配列に追加します。必要に応じて配列のサイズを大きくします。結果配列を返します。
appendIfUnique <array> <value>
値がまだ配列内にない場合は、この値を配列に追加します。
値を追加した場合は true、それ以外の場合は false を返します。
3ds Max 2008 以降 で使用可能です。従来、無償の Avguard 機能拡張として提供されていた機能です。
例:
|
(
local h = #(1,2,3,4)
appendIfUnique h 60 -- should work
appendIfUnique h 4 -- shouldn't work
if ((h.count != 5) or (h[5] != 60)) do
throw "Entry 5 in the array is incorrect"
format "%\n" h
)
|
結果:
|
|
ソース配列から一意のメンバだけを入れた新しい配列を返します。
3ds Max 2008 以降 で使用可能です。従来、無償の Avguard 機能拡張として提供されていた機能です。
例:
|
makeUniqueArray # (1, 2, 3, 4, 3, 2, 1)
|
結果:
|
|
copy <array> [#noMap] -- mapped
配列内のすべての要素のコピーを作成します。 OK を返します。
#noMap が指定される場合、作成されたコピーは「シャロー コピー」と呼ばれ、上位レベルの値( array 値)のみが作成されます。コピーは、配列に格納された値ではなく、両方の array 値に格納された値と等しい参照です。
ネストされたサブ配列を含む配列内の全要素のコピーを作成し、結果として新しい配列を返します。
3ds Max 9 以降 で使用可能です。
コピーおよびディープ コピーの例:
|
--REFERENCING WITHOUT COPYING:
a = #(10,# (1,2,3),30,"test") --create an array
--> # (10, # (1, 2, 3), 30, "test")
b = a --array is b is referencing the same array a
--> # (10, # (1, 2, 3), 30, "test")
b[1] = 5 --so if we change an element in b, a will also change!
--> 5
a
--> # (5, # (1, 2, 3), 30, "test")
b
--> # (5, #(1, 2, 3), 30, "test")
|
--#NOMAP SHALLOW COPY - TOP-LEVEL VALUE COPIED:
a = # (10,#(1,2,3),30,"test") --create an array
--> #(10, #(1, 2, 3), 30, "test")
b = copy a #nomap --create a shallow copy
--> #(10, #(1, 2, 3), 30, "test")
b[1] = 5 --changing a top-level element in b does not change a:
--> 5
b
--> #(5, #(1, 2, 3), 30, "test")
a
--> #(10, #(1, 2, 3), 30, "test")
--But if we change the sub-array in b, a will also change.
--This is because the sub-arrays are not copied recursively.
--See deepcopy() below for a solution.
b[2][2] = 42
--> 42
b
--> #(5, #(1, 42, 3), 30, "test")
a
--> #(10, #(1, 42, 3), 30, "test")
--Same applies to the string which is similar to an array
b[4][2] = "E" -- changing the second character in b affects a:
--> "E"
b
--> #(5, #(1, 42, 3), 30, "tEst")
a
--> #(10, #(1, 42, 3), 30, "tEst")
|
-- #DEEPCOPY COPY - ALL LEVELS COPIED:
a = # (10,# (1,2,3),30,"test") -- create an array
--> # (10, # (1, 2, 3), 30, "test")
b = deepcopy a -- create a deep copy
--> # (10, # (1, 2, 3), 30, "test")
b[1] = 5 -- changing a value in array b has no effect on array a
--> 5
b
--> # (5, # (1, 2, 3), 30, "test")
a
--> # (10, # (1, 2, 3), 30, "test")
b[2][2] = 42 -- changing a sub-array value in b has no effect on a
--> 42
b
--> # (5, # (1, 42, 3), 30, "test")
a
--> # (10, # (1, 2, 3), 30, "test")
-- Same applies to the string which is similar to an array
b[4][2] = "E" -- changing the string in b does NOT affect a:
--> "E"
b
--> # (5, # (1, 42, 3), 30, "tEst")
a
--> # (10, # (1, 2, 3), 30, "test")
|
等価比較式を 2 つの配列で使用すると、1 つがもう 1 つのディープ コピーである場合には、要素ごとの比較は実行せずに 2 つの変数がメモリ内の同じデータを参照しているかチェックします。そのため、このような比較の結果は、2
つの変数が同じメモリ アドレスをポイントしない限り常に false となります。
例:
|
a = # (1,2,3,4,5,10,42) --create an array and store in variable a
--> # (1, 2, 3, 4, 5, 10, 42)
b = deepcopy a --deep-copy the array into variable b
--> # (1, 2, 3, 4, 5, 10, 42)
a == b --although the two have the same elements, they are not considered equal
--> false
c = a --if variable c is referencing the same array in memory,
--> # (1, 2, 3, 4, 5, 10, 42)
c == a --the two are considered equal
--> true
--To compare arrays a and b, we have to compare their elements.
--If no elements are compound values (like other arrays, matrix3 values etc.),
--the following expression would work:
a.count == b.count AND \
(for i = 1 to a.count where a[i]!=b[i] collect i).count == 0
--> true
--A simpler alternative is to compare the two arrays by turning them into strings.
--We have to use the 'with PrintAllElements on' context because otherwise
--longer arrays would be shortened after the 20th element:
with PrintAllElements on a as string == b as string
--> true
|
deleteItem <array> <number>
number でインデックス指定された要素を配列から削除し、配列のサイズを 1 つずつ縮めます。結果配列を返します。
join <array> <collection>
2 番目の引数の要素をすべて最初の引数 array に追加します。
insertItem <value> <array> <integer>
配列内の指定されたインデックスの値を挿入します。配列は必要に応じて拡張されます。OK を返します。
配列内の要素とターゲット オブジェクトの間で MAXScript の「==」比較を行い、指定された値が配列内で最初に現れるインデックスを返すか、その値が配列内になければ ゼロを返します。
Point3 や文字列などの値は、内容が同じであれば一致します。
配列の要素を昇順にソートします。要素はすべて比較可能でなければなりません。
qsort <array> <function> [start:<integer>] [end:<integer>] [user-defined key args passed to function]
指定された関数を使用し、要素ごとに比較しながら配列をソートします。
比較関数には、引数として 2 つの値が必要です。最初の値が 2 番目の値より小さい場合は、0 (ゼロ)より小さい整数値を返します。2 つの値が等しい場合は、0 (ゼロ)を返します。最初の値が
2 番目の値より大きい場合は、0 (ゼロ)より大きい整数値を返します。
開始値または終了値を指定しない限り、配列全体がソートされます。
例:
|
次のスクリプトは、ランダムな 10 個の位置を生成し、[0,0,0]からの距離に基づいて位置をソートします。
|
positions =for i = 1 to 10 collect (random [0,0,0] [100,100,0])
fn compareFN v1 v2 =
(
local d = (length v1)-(length v2)
case of
(
(d < 0.): -1
(d > 0.): 1
default: 0
)
)
qsort positions compareFN
for p in positions do print p
|
キー引数は、すべて比較関数に渡されます。これにより、インデックスで指定されたソートと同様の処理を実行できます。
例:
|
fn compareFN v1 v2 valArray: =
(
local v1i = valArray[v1]
local v2i = valArray[v2]
local d = (length v1i) - (length v2i)
case of
(
(d < 0.): -1
(d > 0.): 1
default: 0
)
)
positions = for i = 1 to 10 collect (random [0,0,0] [100,100,0] )
indexArray = for i = 1 to positions.count collect i
qsort indexArray compareFN valArray:positions
for i = 1 to positions.count do print positions [indexArray[i] ]
|
bsearch <key> <array> <function> [start:<integer>] [end:<integer>] [index:&variable] [user-defined key args passed to function]
配列をソートしたリスト内でのバイナリ検索を実行し、見つかった要素を返します。一致する要素がない場合は定義されません。3ds Max 2009 以降では、検索キーによって配列内の項目を効率的に検索するために使用することができます。
<key> 引数は、検索キーを定義します。これは、検索関数に 1 番目の引数として渡されます。
<array> 引数は、キーの検索先となる配列です。
<function> 引数は、ユーザ定義の検索関数です。既定値では、検索キーと現在の配列要素の 2 つの引数が渡されます。この関数は、このページで前述されている qsort() メソッドで使用する関数と似ています。
この関数で追加のオプション キーワード引数を定義し、bsearch の呼び出しから名前でこれに渡すことができます。
start: および end: オプション キーワード引数を使用すると、検索を特定の範囲に制限できます。
例:
|
-- Compare function used by bsearch:
fn LookupTableComparator a b = (
if a[1] > b[1] then 1
else if a[1] < b[1] then -1
else 0
)
searchTable =for i = 1 to 10 collect #( (i as string), "Test"+(i as string), i)
bsearch "4" searchTable LookupTableComparator
-- output: #("4", "Test4", 4)
bsearch "0" searchTable LookupTableComparator
-- output: undefined
|
3ds Max 2015 以降、オプションの by-reference キーワード引数 index: を指定した場合、見つかった項目のインデックスがそこに作成されます。
3ds Max2015 より前のバージョンでは、同じ結果を得るには、次のような間接的な方法があります。
例:
|
fn compareFn val index valArray: =
(
arrayVal = valArray[index]
format "val = % index = % arrayVal = %\n" val index arrayVal
-- do comparison of val and arrayVal here:
case of (
(val < arrayVal): -1
(val > arrayVal): 1
default: 0
)
)
--valArray is the sorted array:
valArray = #(1, 16, 20, 22, 30, 100, 234)
indexArray = for i = 1 to valArray.count collect i
foundIndex = bsearch 20 indexArray compareFn valArray:valArray
format "Index was %\n" foundIndex
--Since bsearch returns the found element, and we are using the indices as elements,
--the variable foundIndex will contain the index of the found element instead of the valArray element itself.
|
出力:
|
compareFn()
#(1, 16, 20, 22, 30, 100, 234)
#(1, 2, 3, 4, 5, 6, 7)
val = 20 index = 4 arrayVal = 22
val = 20 index = 2 arrayVal = 16
val = 20 index = 3 arrayVal = 20
3
Index was 3
OK
|
MAXScript ベースの検索関数と比較した bsearch() のその他の例と詳細なベンチマークについては、MAXScript に関する質問と回答セクションの「処理速度を上げる方法」にある「Bsearch を使用してテーブル検索を高速化する」を参照してください。
amin ( <array> | {value} )
配列内の項目の最小値または引数の最小値を返します。配列のサイズがゼロの場合または引数が指定されていない場合は、未定義の値が返されます。
例:
|
myMin1 = amin #(5,1,4,2,8)
myMin2 = amin 5 1 4 2 8
|
amax ( <array> | {value} )
配列内の項目の最大値または引数の最大値を返します。配列のサイズがゼロの場合または引数が指定されていない場合は、未定義の値が返されます。
注:参照代入で解説されているように、配列内の要素は値ではなく参照によって保存されます。配列要素に直接操作するとき、配列への参照が複数の場所に格納されていると、予期しない結果が発生することがあります。次のスクリプトに例を示します。
スクリプト:
|
RandomSets=#() -- create array
RandomSet=#() -- create array
for i = 1 to 3 do -- loop i
(for j=1 to 3 do -- for each i loop j
RandomSet[j] = random 1 100 -- set array element to random value
print RandomSet #nomap -- print array of random values
RandomSets[i] = RandomSet -- store random value array in array
)
print RandomSets -- print array of arrays of random values
|
出力:
|
#() -- result of line 1
#() -- result of line 2
#(33, 47, 31) -- output from line 6, i =1
#(4, 52, 39) -- output from line 6, i =2
#(52, 36, 23) -- output from line 6, i =3
OK -- result of for loop, lines 3 to 8
#(52, 36, 23) -- 3 lines of output from line 7
#(52, 36, 23)
#(52, 36, 23)
OK -- result of line 9
|
出力結果の 7 ~ 9 行目を見ると、配列 RandomSets の全要素に同じ値が含まれていることがわかります。 しかし、これは出力結果の 3 ~ 5 行目から予想された結果とは異なります。 RandomSets の各要素に同じ値が格納されているのは、 RandomSet に対して実際には 1 つの配列値しか作成されておらず(2 行目)、単にその配列値の要素を変更した(5 行目)にすぎないからです。つまり、 RandomSets の各要素は、実際には同じ値をポイントしています。
上記のスクリプトを修正する簡単な方法は、2 行目を for i ループ式の中に移動することです。 これで、新しい配列値が作成され RandomSets に格納されます。
|
スクリプト:
|
RandomSets =# ()
for i = 1 to 3 do
( RandomSet =# () --create a new array value for RandomSet
for j = 1 to 3 do
RandomSet[j]=random 1 100
print RandomSet #nomap
RandomSets[i]=RandomSet
)
print RandomSets
|
出力:
|
#()
#(89, 27, 88)
#(87, 87, 10)
#(74, 27, 64)
OK
#(89, 27, 88)
#(87, 87, 10)
#(74, 27, 64)
OK
|
オブジェクト セットとワイルド カード パス名は、 as 演算子を使うと配列に変換できます。
これには、そのセット内またはそのパス名と一致するものの中にある現在のオブジェクト配列の「スナップショット」を取る効果があります。つまり、そのセットまたは一致条件が変更されたかどうかを気にすることなく、後でそのオブジェクトのコレクションで作業ができるのです。
これは、3ds Max UI での名前の付いた選択セットに似ているので、これを使うと、たとえば MAXScript とインタラクティブに作業をする場合などに選択を追跡できます。
シーンから配列内のオブジェクトをユーザが削除した場合、その配列上に演算をマップしようとするとエラーとなります。
例:
|
sel1 = selection as array
boxes_at_load = $box* as array
snap_children = $torso...* as array
original_cameras = cameras as array
|
ガベージ コレクションを待たずに、配列の値によって使用されているメモリを解放します。
3ds Max 9 以降で使用可能です。