関数の実行ごとに名前空間が違う、と言えばいいのか
同じ関数を実行してもローカル変数はそれぞれが持ちますよね
PHP
1function hoge(){ 2 if(!isset($b))$b=0; 3 echo $b++; 4} 5hoge(); 6hoge(); 7hoge();
これの出力は000で012とかになったりはしません
これっておよそどの言語でも同じなんでしょうか?
それとも関数ごとで変数を共有するような言語もあったりするんでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
ベストアンサー
これは名前空間と言うより、変数のスコープとライフサイクルのお話です。
スコープ
ほとんどの言語には変数にスコープというのがあります。スコープというのはその変数が「見える場所はどこか」ということです。スコープは大きく分けると二つです。
- グローバルスコープ: プログラムのどこからでも見えるスコープです。言語によってはそのままどこでも使える場合と、関数内等で使用する場合は何かしらの宣言やプリフィックス等を付ける必要がある場合があります。更に分けると2種類あります。
- グローバルスコープ: 他のライブラリやソースファイルからもどこからでも見えます。グローバル変数と呼びます。
- ファイルスコープ: そのプログラムのファイル内でのみ見えます。採用している言語は少なく、C/C++ぐらいです。こちらもグローバル変数と呼ぶのですが、区別する場合はC/C++で
static
をつけることから静的グローバル変数(言葉の意味としてはまったくもってよくわからないのですが)と呼ばれることもあります。
- ローカルスコープ: ある特定の範囲内でのみ見えるスコープです。更に分けると4種類あります。
- 関数スコープ: その関数内でのみ見えます。ローカル変数と呼ばれます。
- ブロックスコープ: そのブロック(例えば、for文の中とか、何がブロックを構成するかは言語によって異なる)でのみ見えます。こちらもローカル変数と呼ばれます。関数スコープと区別してブロックローカル変数と呼ぶ場合もあります。
- クラススコープ: クラスとそのクラスのインスタンスから見えます。基本的にはクラスとインスタンスで共用されます。クラス変数と呼ばれます。
- インスタンススコープ: インスタンスでのみ見えます。基本的にはインスタンス毎に別々に持ちます。インスタンス変数と呼ばれます。
各スコープにおいて、見える範囲が異なれば、名前空間が異なるようになります。同じ名前を付けても、別のものとして扱います。
言語によっては、全てのスコープが用意されているわけではありません。
ライフサイクル
変数のライフサイクルとはいつ生成されていつ破棄されるかです。スコープによって動作が異なります。
- グローバルスコープ: プログラム開始時または宣言時やライブラリのロード時に生成されます。明示的に破棄しない限り、プログラムが終了するまで破棄されることはありません。
- ローカルスコープ: 大きく自動(auto)と静的(static)に分かれます。
- 自動: 該当するスコープ開始時(関数スコープであれば関数開始時)または宣言時に生成されます。ほとんどの場合は、スコープ終了時に自動的に破棄されます。ほとんどの変数がこれに該当します。
- 静的: プログラム開始時または該当するスコープのロード時に生成され、プログラム終了時または該当するスコープ自体の破棄に破棄されます。宣言時に静的であるとした場合や、クラススコープが該当します。ローカル変数のうち静的な場合を区別して静的ローカル変数とよぶます。
言語によって、細かい動作が異なります。静的ローカル変数が用意されていない言語もあります。
宣言の仕方
どの変数がどれになるかは最初の宣言の場所と宣言の仕方によって異なります。例えば、PHPの場合、関数外で宣言すればグローバルスコープに、関数内で宣言すれば関数スコープになります。また、関数スコープの変数にstatic
を付けると静的になります。
質問で書いているPHPのコードにおいて、$b
はただのローカル変数です。関数内でしか見えず、かつライフサイクルは自動になりますので、関数終了時に自動的に破棄されます。そのため、毎回0で初期化されるため、出力が000になったのです。では、出力が012になるようにPHPを修正する例を二つあげます。
- グローバル変数を使う。
PHP
1<?php 2$b = 0; 3function hoge(){ 4 global $b; 5 echo $b++; 6} 7hoge(); 8hoge(); 9hoge();
$b
はグローバル変数になります。関数内で使う場合は、global $b;
としておく必要があります。グローバル変数はプログラム全体で一つだけであり、自動的に破棄されません。ですので、数はインクリメントされていきます。
- 静的ローカル変数を使う。
PHP
1<?php 2function hoge(){ 3 static $b = 0; 4 echo $b++; 5} 6hoge(); 7hoge(); 8hoge();
ロカール変数にstatic
修飾子を付けているため、$b
は静的ローカル変数になります。静的ローカル変数は自動的に破棄されなくなり、二回目の時は再利用されます(0を代入するという初期化は最初の一回のみで、二回目以降はされません)。ですので、こちらもインクリメントされます。
PHPでのさらにより詳しい動作は下記マニュアルを参考にしてみてください。
参考文献: PHP マニュアル: 変数のスコープ
言語によって宣言の仕方や使い方が違いますが、概念はほぼ同じ、同じようなことができるようになっています。各言語のマニュアル等を参考にしてください。
投稿2015/10/09 23:07
編集2015/10/09 23:23総合スコア21733
0
多くの言語では、基本的には、関数のローカル変数は、関数が呼び出された時点で新規に作成されますので、毎回異なる実体と言うことになります。
言語によっては、毎回作成されるので無く、プログラム実行ごとに1つ、つまり、複数回の関数呼び出しでも同じ実体を共有するようなローカル変数を宣言することも出来ます。例えばCだと、static修飾子をつけるとそうなります。
例:
C
1#include <stdio.h> 2int foo(){ 3 static int x=0; 4 x++; 5 return x; 6} 7 8main(){ 9 printf("%d\n",foo()); // => 1 を出力 10 printf("%d\n",foo()); // => 2 を出力 11 printf("%d\n",foo()); // => 3 を出力 12}
現代の多くの言語と違って、fortranだと、特に指定しなくても実体は1つです。
fortran
1 integer function foo() result(ret) 2 integer :: x = 0 3 x = x + 1 4 ret = x 5 end 6 7 integer :: foo 8 print *, foo() 1を出力 9 print *, foo() 2を出力 10 print *, foo() 3を出力 11 end
投稿2015/10/09 22:49
編集2015/10/10 00:14総合スコア84423
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
これっておよそどの言語でも同じなんでしょうか?
概ねYesです。
少ない理由は関数のスコープを独立させないと使い勝手が悪いからです。
ただ言語は自由に作れますし、その挙動は仕様次第なので存在し得ないわけではありません。
これはフェイクですが、クロージャーは一見禁を破っているように見えますね。
javascript
1//jQuery 2$(function(){ 3 a=1; 4 function b(){ 5 console.log(a); 6 a++; 7 } 8 b(); // 1 9 b(); // 2 10 b(); // 3 11});
他には確か8bitPCの頃のベーシックなんかは、サブルーチンの変数はメインルーチンの変数と共用だったと思います。(うろ覚え)
Interpreterデザインパターンの習作として自作したスクリプト言語なんかだと、ユーザ定義関数機能を実装しながら面倒でスコープを実装せずに終わったものなど幾らでも存在すると思います。
投稿2015/10/09 18:47
編集2015/10/09 18:50総合スコア2068
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/09 21:55
2015/10/10 06:29
0
名前空間を出す当たり、オブジェクトの話をしようとしているように見えますが
例文は、オブジェクトとは関係ないです。
hoge();で、
hoge()メソッドが呼び出され、
変数bが未定義であれば、b=0で定義
標準出力へbを出力し、
bをインクリメントする。
そして処理が終わるので、生成した変数は破棄される。
というのが3回繰り返されるという実装ですね。
同じローカル変数ですが、
b=0で定義→破棄
を繰り返すので、000という出力になります。
繰り返しになりますが、
たとえ暗黙・無意識でも、定義と破棄をする実装をしています。
言語が違っても、定義と破棄をする実装であれば、結果は同じになります。
投稿2015/10/09 18:03
総合スコア1124
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/10 06:40
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/10 06:09