変数のスコープ

変数には「スコープ」と呼ばれるアトリビュートがあり、MAXScript コード内で変数がアクセス可能な場所を決定します。

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

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

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

明示的な変数宣言

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

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

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

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

<var_name> [ = <expr> ] -- name and optional initial value

例:

global baz -- initialized to 'undefined'
global foo=10, initialized=false -- initialized globals
local x = 1, -- continued on several lines
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 -- scope context: global
( b=20 -- new scope context: level 1
for i = 1 to 5 do -- new scope context: level 2
( j=random i a
k=random i b
print (j*a+k*b)
) -- end of scope context: level 2
a=a+b
) -- end of scope context: level 1
print a -- back to global scope context
print k

結果 #1

コメント

10
170
120
290
320
360
30
30
30
undefined
undefined
result of a=10
print from i=1
print from i=2
print from i=3
print from i=4
print from i=5
return value of end of scope level 1
result of print a
return value of print a
result of print k
return value of k

結果 #2

コメント

10
170
420
410
180
160
30
30
30
5
5
result of a=10
result of print from loop i=1
result of print from loop i=2
result of print from loop i=3
result of print from loop i=4
result of print from loop i=5
return value at end of scope level 1
result of print a
return value of print a
result of print k
return value of 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 -- explicit declaration of foo and x,
-- scope is global
y = 10 -- implicit declaration of y,
-- scope is global
format "context level global: foo= %\n" foo
if x > y then
( -- top-level open parentheses
-- new scope is created
local baz = foo + 1 -- uses the global foo
local foo = y - 1 -- declares 1st local foo, hides global foo
format "context level 1: foo= %\n" foo
b=box() -- b implicitly declared as local
b.pos.x = foo -- uses 1st local foo
if (foo > 0) then
(
local a
local foo = y - x -- a nested 2nd local foo, hides 1st local
format " context level 2: foo= %\n" foo
a = sin foo -- uses 2nd local foo
format "a= %\n" a
) -- leave scope
b.pos.y= foo - 1.5 -- back to 1st local foo
format "context level 1: foo= %\n" foo
) -- leave scope
-- b, baz and foo no longer in scope
format "context level global: foo= %\n" foo -- back to global foo

 

出力

 

20 -- result of line 1
10 -- result of line 3
context level global - foo= 23 -- output from line 5
OK -- result of line 5
context level 1: foo= 9 -- output from line 11
context level 2: foo= -10 -- output from line 18
a= -0.173648 -- output from line 20
context level 1: foo= -10 -- output from line 23
OK -- result of if expression lines 6 to 24
context level global: foo= 23 -- output from line 26
OK -- result of line 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

関連事項