変数のスコープ
 
 
 

変数には「スコープ」と呼ばれる属性があり、MAXScript コード内で変数がアクセス可能な範囲を決定します。

MAXScript には、グローバルとローカルの 2 種類の変数スコープがあります。

グローバル変数」は実行中のすべての MAXScript コードで見ることができ、3ds Max を終了するまで値を保持します。

ローカル変数」は、現在有効な構文スコープ内のコードに対してのみ直接可視となり、現在のスコープが有効な場合に限り、値が保持されます。スコープが有効でなくなると、そのスコープにローカルの変数にアクセスできなくなります。これは、多くのプログラミング言語に実装されている変数の概念と同様です。次に、MAXScript が新しいスコープを作成する条件について説明します。

明示的な変数宣言

MAXScript では、明示的に宣言したり、グローバルとローカル変数の両方を初期化するために使用する言語構成が提供されています。

次に変数宣言 <variable_decls> の構文を示します。

( local | global ) <decl> { , <decl> }

ここで、 <decl> は次のように定義されます。

<var_name> [ = <expr> ] -- 名前およびオプションの初期値

例:

global baz -- 「undefined」に初期化
global foo=10, initialized=false -- 初期化したグローバル
local x = 1, -- 複数行に継続
y = 2,
z = sqrt(123.7)

前述のように、ローカル変数はスコープが有効な限り値を保持します。ネストされているスコープでは、保持される期間は非常に短く、関数、ループ式、またはブロック式の実行開始から実行終了までです。

スコープが実行範囲に入るたびに、ローカルの新規セットが作成され、スコープの終了時に削除されます。

変数を常に明示的に宣言することは推奨される方法です。その理由のいくつかについてはこのページを参照してください。

明示的なグローバル変数のアクセス

3ds Max 8 以降、MAXScript は変数名をグローバルとして明示的にアクセスする方法を提供しています。変数のグローバルのプール内に存在している場合に使用されます。変数がグローバル スコープ内に存在していない場合、明示的にグローバルなものとして作成されます。

構文はダブルコロンの'::'で、このトピックで記述されます。

「プライベート グローバル」変数としてのローカル変数

状況によってはトップ レベルのスコープの有効期間を延長でき、トップ レベルで宣言されたローカルは値をその延長期間にわたって保持できます。

特に、スクリプト化されたロールアウト、ユーティリティ、プラグイン、スクリプト コントローラ、マクロ スクリプトで宣言されたトップ レベルのローカルは、ロールアウト、ユーティリティ、プラグイン、マクロ スクリプトが存在する限り値を保持します。

通常、これらのローカルは再定義されるまで存在し続けます。たとえば、マクロ スクリプトを定義して初めて実行するときトップ レベルのスコープが作成され、マクロ スクリプトを再定義しない限り、継続実行数を超えるまで有効になります。この時点で、既存のトップレベル スコープが破棄され、新しいスコープ(およびそのトップレベル ローカル)が作成されます。

これにより、これらの構文のトップ レベル ローカルをプライベート グローバルとして使用できます。保持される値は実行中はそのまま残りますが、構文内のコードのみが変数を参照するため、他のグローバルと競合することはありません。

スクリプト化されたプラグインでは、プラグインの代わりにトップレベル ローカル スコープの別のコピーが「個々に」作成されます。このスコープは、そのインスタンスが残っている間、現在の 3ds Max セッションが終了するまで有効です。

履歴:

3ds Max R2.x では、宣言が新しい変数を作成する場合に限り、オプションの初期化値がグローバル変数に適用されます。たとえば、以下のスクリプトを 2 回実行すると、2 行目の x の値は、最初の実行では 10 になり、2 回目の実行では 20 になります。3ds Max 3 以降では、オプションの初期値は、常にグローバル変数に適用されます。
global x = 10
print x
x=20

3ds Max 3 以降でグローバル変数の初期化条件文を作成したい場合は、次のような構文を使用します。

global foo; if foo = = undefined do foo = 23

暗黙的な変数宣言

変数を明示的に宣言せず、その変数名が上のレベルのスコープに存在しない場合は、その変数を初めて使用するとき MAXScript によって変数が作成され、特別な値 undefined を保持するために変数が初期化されます。変数を使用する前に、変数を明示的に宣言したり値を初期化する必要はありません。前のステートメントで明示的に宣言されていない変数は、暗示宣言変数と呼ばれます。暗示的に宣言された変数のスコープは、変数が最初に使用されたときの MAXScript のスコープ コンテキストになります。MAXScript スコープ コンテキストの初期値はグローバルで、次の場合に新しいスコープ コンテキストが開かれます。

これらの新規スコープ内では、新しく参照された変数は、ローカル変数として明示的に宣言されます。

スコープ コンテキストはネストされています。明示的または暗示的に宣言された変数のスコープが 1 つのスコープ コンテキストのレベルになり、その下のレベルのスコープ コンテキストすべてに拡張されます。

次に、スクリプトの例を示します。

行 #

1
2
3
4
5
6
7
8
9
10
11
a=10 -- スコープ コンテキスト: グローバル
( b=20 -- 新規のスコープ コンテキスト: レベル 1
for i = 1 to 5 do -- 新規のスコープ コンテキスト: レベル 2
( j=random i a
k=random i b
print (j*a+k*b)
) -- end of scope context: level 2
a=a+b
) -- スコープ コンテキストの終わり: レベル 1
print a -- グローバル スコープ コンテキストに戻る
print k

結果 #1

コメント

10
170
120
290
320
360
30
30
30
undefined
undefined
a=10 の結果
i=1 から印刷
i=2 から印刷
i=3 から印刷
i=4 から印刷
i=5 から印刷
スコープ レベル 1の最後の値を返します
印刷した a の結果
印刷した a の値を返します
印刷した k の結果
k の値を返します

結果 #2

コメント

10
170
420
410
180
160
30
30
30
5
5
a=10 の結果
i=1 ループから印刷した結果
i=2 ループから印刷した結果
i=3 ループから印刷した結果
i=4 ループから印刷した結果
i=5 ループから印刷した結果
スコープ レベル 1の最後の値を返します
a を印刷した結果
印刷した a の値を返します
印刷した k の結果
k の値を返します

上のスクリプトで、変数 a はグローバル スコープ コンテキストで最初に使用され、スコープにはスコープ コンテキスト グローバル、レベル 1、およびレベル 2 が含まれます。

変数 b は、スコープ コンテキスト レベル 1 で最初に使用されるのでローカル変数として明示的に宣言され、スコープにはレベル 1 とレベル 2 のスコープ コンテキストが含まれます。

変数 i および j はスコープ コンテキスト レベル 2 で最初に使用され、スコープはスコープ コンテキスト レベル 2 のみです。

変数 k のスコープは、このスクリプトが既に実行されたかどうかにより異なります。

初めてスクリプトを実行した場合、変数 k は最初に 5 行目で定義され、スコープはスコープ コンテキスト レベル 2 になります。

また、変数 k は 11 行目で再度使用されています。5 行目で定義された変数 k は、ここではスコープ内にないため、新しい変数 k が定義され、スコープはグローバルになります。

2 回目にスクリプトを実行する場合、5 行目で MAXScript により変数 k が既に存在することが検出され、既存の変数 k が使用されます。

このため、最初にスクリプトを実行すると、11 行目で undefined が出力されますが、2 回目にスクリプトを実行すると、11 行目では 5 行目で計算された最後の値が出力されます。

注: for ループ カウンタで使用される変数(上のスクリプトでは変数 i )は、特殊なケースです。 for ループ カウンタ変数のスコープは、常に for ループ用に作成されたスコープ コンテキストです。これは、 for ループ カウンタ変数の名前が、上位のスコープ コンテキストで既に暗黙的または明示的に宣言されている場合にも当てはまります。

構文スコープ

ローカル変数を明示的に宣言する場合、上のスコープ コンテキスト レベルで使用した変数と同じ名前を再使用できます。

この場合、新しく宣言したローカル変数は、同じ名前の外部変数を不可視にします。

ローカル変数のスコープ内でのこの変数名への参照は、以後この新しいローカル変数が使われます。

ローカル変数のスコープの最後に到達すると、次の外部変数が再び可視になります。

この可視手順は「構文スコープ」と呼ばれます。次のスクリプトに構文スコープの例を示します。

#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
global foo = 23, x = 20 -- foo と x の明示的な宣言
-- スコープはグローバル
y = 10 -- y の暗示的な宣言
-- スコープはグローバル
format "context level global: foo= %\n" foo
if x > y then
( -- トップレベルの始まりのカッコ
-- 新規のスコープを作成
local baz = foo + 1 -- グローバルの foo を使用
local foo = y - 1 -- 最初のローカルの foo を宣言し、グローバルの foo を不可視にする
format "context level 1: foo= %\n" foo
b=box() -- b をローカルとして暗示的に宣言
b.pos.x = foo -- 最初のローカルの foo を使用
if (foo > 0) then
(
local a
local foo = y - x -- ネストされた 2 番目のローカル foo、最初のローカルの foo を不可視にする
format " context level 2: foo= %\n" foo
a = sin foo -- 2 番目のローカルの foo を使用
format "a= %\n" a
) -- スコープの終わり
b.pos.y= foo - 1.5 -- 最初のローカルの foo に戻る
format "context level 1: foo= %\n" foo
) -- スコープの終わり
-- b、baz、および foo はスコープ外になる
format "context level global: foo= %\n" foo -- グローバルの foo に戻る

 

[出力]

 

20 -- 1 行目の結果
10 -- 3 行目の結果
context level global - foo= 23 -- 5 行目からの出力
OK -- 5 行目の結果
context level 1: foo= 9 -- 11 行目からの出力
context level 2: foo= -10 -- 18 行目からの出力
a= -0.173648 -- 20 行目からの出力
context level 1: foo= -10 -- 23 行目からの出力
OK -- 6 ~ 24 行目の if 式の結果
context level global: foo= 23 -- 26 行目の出力
OK -- 26 行目の結果

1 つの変数名を何度も使うことに違和感があるかもしれませんが、大型のプログラムの場合これらのスコーピング規則が便利な場合もあります。

たとえば、新しいユーザ インタフェース アイテムとそのハンドラをユーティリティ スクリプトに追加する場合、ハンドラに使用する変数をローカルとして明示的に宣言すると、これらの変数がスクリプト内のほかの場所で使用されている同名の変数に依存しないことが保証されます。

スクリプトを作成するときは、ローカル変数およびグローバル変数を明示的に宣言することをお勧めします

暗示的な宣言は簡易的に提供されるものであり、通常、リスナーでインタラクティブに作業する場合や短いスクリプトを作成する場合に使用します。

長いスクリプトを作成する場合は、変数を明示的に宣言するとエラーが軽減し、コードが読みやすくなります。

また、特にグローバル変数を必要な場合を除き、すべての変数をローカルとして宣言することをお勧めします。これは、次のような理由によります。

(if false do box=10; box)

次のスクリプトでは、定義されていない変数 k を 7 行目で使用することにより、エラーが挿入されています。出力では、エラー コール スタックのトレースバックにより、関数 afunc 内と、 afunc を呼び出しているブロック式での変数 b の値が表示されます。

スクリプト:

fn afunc =
(
local b
b = box()
for i in 1 to 10 do
for j in 1 to 10 do
instance b pos:[i*20,j*30,30*sin(k*36)]
)
--
(
global c=10
local b
b="hello"
afunc()
)

出力:

afunc()
-- Error occurred in j loop
-- Frame:
-- k: undefined
-- j: 1
-- called in i loop
-- Frame:
-- i: 1
-- called in afunc()
-- Frame:
-- b: $Box:Box102 @ [0.000000,0.000000,0.000000]
-- called in <block>()
-- Frame:
-- b: "hello"
-- No ""*"" function for undefined
OK

3 行目と 12 行目を削除してこのスクリプトを実行すると、出力結果は次のようになります。関数 afunc とブロック式は別の MAXScript スコープ コンテキストに存在するため、変数 b はそれぞれローカルとして暗示的に宣言され、異なる値を持ちます。ただし、これらは暗示的に定義されるため、エラー コールスタック トレースバックには表示されません。

出力:

afunc()
-- Error occurred in j loop
-- Frame:
-- k: undefined
-- j: 1
-- called in i loop
-- Frame:
-- i: 1
-- called in afunc()
-- Frame:
-- No ""*"" function for undefined
OK
関連事項