どうも。つじけ(tsujikenzo)です。このシリーズではプログラミング言語の「スコープ」について全3回でお届けしています。今日は最終回で「静的スコープ」についてです。私もとても難しいと思いながら書いてます💦
前回のおさらい
前回は、「動的スコープ」についてお届けしました。動的スコープの[変数名対応表:動的スコープ(仮)]は「プロジェクトのどこからでも呼び出せるけど、呼び出した先の変数も変更してしまう」という問題が発生しました。
今回は「静的スコープ」についてお届けします。
静的スコープを生成する
動的スコープの問題点のおさらい
関数getName()で変数name = ‘Etau’が定義されると、「変数名対応表:動的スコープ(仮)」が作成されました。
そのまま、9行目の関数myFunction()の呼び出しを実行します。
関数myFunction()内ではconsole.log(name)で変数nameの値を確認するコードが書かれていますが、「変数名対応表:動的スコープ(仮)」が手前にあるため、Etauを返します。
私たちの直感としては、関数myFunction()にとってnameはTsujikeであった方が、自然でしょう。(Etauの方が自然だと感じる人もいるかもしれませんので否定はしません。)
値はTsujikeであろう変数nameは、呼び出された元の関数(getName())によってEtauになってしまうことは、呼び出した側にも、呼び出された側にも、精神的に良くありません。
関数専用の「変数名対応表」を作成する
動的スコープがレイヤーを一枚上に重ねた「変数名対応表」を作成することが問題だとしたら、関数専用の「変数名対応表」を作成するアイディアはいかがでしょうか。
具体的には、「関数getName()にとってnameはEtau」、「関数myFunction()にとってnameはmyFunction()内で定義されたname、もしくは、グローバルエリアで定義された値(ここではTsujike)」を参照できるような「変数名対応表」を作成する作戦です。
これで、呼びだされた関数に影響をうけることなく、「変数名対応表」を参照できるようになりました。
このように、関数専用に作られた「変数名対応表」を「静的スコープ」といいます。静的スコープも関数が定義された時に生成され、関数を抜けると破棄されます。
そして、静的スコープは関数だけでなく、モジュール、プロシージャ、プロジェクトといった処理の固まりによって変数の影響範囲を管理することを覚えておいてください。
静的スコープの作り方
では、どのようにして静的スコープを作るのでしょうか。
それは、変数を定義する際に、[var][let][const]など(VBAではDim,Public,Private,Staticなど)の宣言をつける(もしくはつけない)ことです。
GASでは変数nameの先頭に[var]を付けることで、
- 「変数名対応表:getName()」を作成する
- 変数nameの適用範囲を「変数名対応表:getName()」のみにする
- getName()内で新しい変数が宣言されたら、「変数名対応表:getName()」にメモする
- getName()を抜けるときに「変数名対応表:getName()」を破棄する
という静的スコープを生成します。
VBAにおけるスコープ
同じことをVBAでも確認しましょう。モジュールレベルにPrivateステートメントで変数xを定義すると[変数名対応表:モジュールレベル](静的スコープ)が生成されます。
静的スコープは処理を抜けると破棄されるので、別のモジュールから参照することができません。
Publicステートメントは処理を抜けても静的スコープを残すことができます(とも言えます)。Publicステートメントで変数を宣言するとプロジェクトのどこ(別モジュール)からでも参照ができるのはこのためです。
くどいようですが、Dimステートメントもモジュールレベルの静的スコープを生成していることがわかります。
モジュールレベルのPrivateステートメントで宣言された変数xと、サブプロシージャMySub()内でDimステートメントメントで定義された変数yの、それぞれの静的スコープをまとめるとこうなります。(DimステートメントはSubプロシージャ内で定義されると、Subプロシージャ内をスコープとします。)
ちょっとマニアックな説明ですが、PrivateやPublicステートメントをモジュールレベル(宣言セクション)で記述するのは、もしくは、Subプロシージャ内でPublicステートメントが使えないのは、静的スコープが破棄されるからです。
VBAでは同様に、プロシージャでも静的スコープを生成することができ、さらにPrivate、Publicを指定することで、スコープの影響範囲を管理することができます。(説明は割愛します)
VBAのスコープまとめ
公式ドキュメントから引用させていただきます。
スコープによって、別のプロシージャから変数、定数、またはプロシージャを使用できるかどうかが決まります。 スコープには、[プロシージャレベル]、[プライベートなモジュールレベル]、[パブリックなモジュールレベル]の 3 つのレベルがあります。
アクセス修飾子
厳密に言うとVBAのPublic,Privateステートメントは「アクセス修飾子」といい、スコープとは違いますが、アクセスする機能の範囲ということでスコープという単語を使いました。
VBAではアクセス可能な範囲がクラスに限定されるprivateと比べて、protectedは子クラスにまで範囲が広がってしまいます。呼び出し元での変更が呼び出し先に影響するということは、動的スコープに似ています。『コーディングを支える技術』
まとめ
さて、最終回として「静的スコープ」をお届けしました。処理(関数、プロシージャ、モジュール、プロジェクト)によって生成されたり破棄されたりする「変数名対応表」を用いて、変数や機能の影響範囲を限定することができました。
現在のプログラミング言語で採用されているスコープは、ほとんど「静的スコープ」です。しかし、一度受け取った要素をよっこして、時間が経ったら戻すという処理はプログラミングの中で数多く登場しますし、「動的スコープ」が用を足さないものという訳ではなさそうです。
また、静的スコープに関しても完璧な存在ではなく、セキュリティと便利性のように「誰でもアクセスできるということは誰でも壊すことができる」という反面を持ち合わせています。これから高度な処理をコーディングする機会も増えるかもしれませんが、都度、基本に立ち返って考えてみたいと思います。
このシリーズで完全に「スコープ」を理解したとはとても思えませんが、少しだけ理解を進められたような気がします。引き続き頑張りたいと思います。
【全体的な補足】
昔のPCは性能が低かったため、メモリをいかに節約するかという研究がスコープが発展してきた背景にもあります。がしかし、最近のPCは性能も高く、我々ノンプログラマーが書くコード量では、メモリのことはあまり気にしなくていいと思いますので、考察には除外させていただきました。