Processingで学ぶ 実践的プログラミング専門課程

第8回 変数の使える範囲を限定する スコープ

この記事を読むのに必要な時間:およそ 5 分

導入

構造化プログラミングの最終回として,スコープについて話します。

かつて,スコープという概念が一般的でなかった頃,変数の取り扱いは大変面倒でした。ある場所で作った変数と同じ名前の変数が別のところで使われると,意図しない場合に値が変更されるのです。ソースコードが大きくなるにつれ,ソフトウエア開発に参加する人数が増すにつれ,変数の管理問題は指数関数的に難易度が高くなります。変数の管理のために帳簿やデータベース,管理の担当部署が必要になるほどでした。

この問題を上手に解決するためにプログラミング言語側に設けられた仕組みがスコープです。Processingなどのオブジェクト指向言語では,クラスやメソッドもスコープを適用できます。スコープを意識し,上手に区別してコードを作成するようにしましょう。

展開

スコープとは

スコープとは,変数やメソッド,クラスなどが使える範囲のことです(⁠見える範囲」とも言います)⁠コードはできる限りシンプルな方が良いため,ある箇所だけで必要なものは他所から使えないようにするべきなのです。スコープの仕組みの無い言語では,プログラマが神経を使って注意深くコードを書く必要があります。スコープの仕組みは,プログラマをストレスから解放してくれるのです。

ここでは変数のスコープに話を絞ります。メソッドやクラスについても同様に考え,極力狭いスコープに収まるようにしましょう。

スコープを限定する方法

スコープを限定する最も基本的な方法は,ブレース(波括弧)で囲むことです。

BasicScope.pde。スコープを限定する方法の基本

int x = 5;

{
  int y = 3;
  println("(x,y) = ("+x+","+y+")");
}

//スコープ外なのでエラーになります
//println("(x,y) = ("+x+","+y+")");

コメントアウトされているprintln文をアンコメントアウトすると,スコープ外で変数yを使用するのでエラーになります。

次のようにメッセージエリアへyが見えません」と表示されます。

The field Component.y is not visible

[作業] sketch BasicScope.pdeを自分の手で入力して実行しましょう。コメントアウトしている行をアンコメントアウトして,エラーを起こしてみましょう。変数xもブレースの中に移動してエラーを発生させましょう。

ブレースで囲まれたコードブロック内で宣言された変数は,そのコードブロックの中でしか参照できません。スコープの仕組みはこのように変数の有効範囲を限定できます。

コードブロックを切り分けていれば,同じ名前の変数を別の型や値で使用することができます。スコープを切り分けなければ,同じ名前の変数名を使わずに,無理に説明的な名前で区別をすることになります。不必要に長い名前付けは避けるべきです。コードを読みにくくします。

明確にコードブロックを切り分けていれば,また,双方のブロックがネストしていない状況ならば,それぞれのコードブロック内で同名の変数を宣言すれば良いでしょう。

SplitScope.pde。コードブロックが別ならば同じ名前の変数が宣言できる

int x = 5;

{
  int y = 3;
  println("(x,y) = ("+x+","+y+")");
}

{
  float y = 6;
  println("(x,y) = ("+x+","+y+")");
}

次のようにネストしたコードブロックでは,同名の変数を宣言できません。

NestedScope.pde。内側のスコープで,外側のスコープと同じ変数は使えない

int x = 5;

{
  int y = 3;
  println("(x,y) = ("+x+","+y+")");
  {
    float y = 6;
    println("(x,y) = ("+x+","+y+")");
  }
}

スコープを限定して変数を使っている例の代表は,forループのカウンタ変数です。次のサンプルコード(GSWPのExample 04-10)をご覧ください。

GSWPのExample 04-10

// Example 04-10 from "Getting Started with Processing" 
// by Reas & Fry. O'Reilly / Make 2010

size(480, 120);
background(0);
smooth();
noStroke();
for (int y = 0; y <= height; y += 40) {
  for (int x = 0; x <= width; x += 40) {
    fill(255, 140);
    ellipse(x, y, 40, 40);
  }
}

2重forループを使ってディスプレイウインドウを円で埋め尽くします。xyは円の中心座標のためだけの変数ですので他所で必要ありません。このコードのようにxyを宣言すると,外側のforのコードブロック内ではyのみ有効となり,内側のforのコードブロックの中ではxy両方が有効となります。

つまり,次のように変数を参照するコードを追加するとエラーになります。

EX04_10_2.pde。エラーを起こすGSWPのExample 04-10

size(480, 120);
background(0);
smooth();
noStroke();
for (int y = 0; y <= height; y += 40) {
  
  print("(x,y) = ("+x+","+y+")"); // エラー!xが使えません。

  for (int x = 0; x <= width; x += 40) {
    fill(255, 140);
    ellipse(x, y, 40, 40);
  }
}

print("(x,y) = ("+x+","+y+")"); // エラー!xもyも使えません。

これをwhile文で書き換えると状況が少し変わります。

EX04_10_WithWhile.pde。Example 04-10をwhile文で書き換えた

size(480, 120);
background(0);
smooth();
noStroke();
// 外側のループ
int y = 0;
while(y <= height){
  // 内側のループ
  int x = 0;
  while(x <= width){
    fill(255, 140);
    ellipse(x, y, 40, 40);
    x += 40;
  }
  y += 40;
}

Example 04-10と同じ動作をさせるためには,変数xのスコープを外側のwhile文のコードブロック内にしなければなりません。同様に,変数yのスコープはこのスケッチ全体となります。for文の利点は,ループの中だけで必要なカウンタ変数のスコープを閉じ込めることができることでしょう。

[作業] EX04_10_WithWhile.pdeを変更して,変数xのスコープも変数yと同じにしましょう。それでsketchの動きに変化があるでしょうか。エラーや問題が発生するでしょうか。エラーや問題が起きないとしたら,この変更の問題点や利点を検討してみましょう。

著者プロフィール

平田敦(ひらたあつし)

地方都市の公立工業高等学校教諭。趣味はプログラミングと日本の端っこ踏破旅行。やがては結城浩氏のような仕事をしたいと妄想している。

Twitter : @hirata_atsushi

コメント

コメントの記入