このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

プログラマの常識:変数と値

プログラマの常識っぽいネタ。

「変数は入れ物で、値は中身」とか僕は説明するんですが、それほど単純でもないですね。



とりあえず即物的に、変数はメモリ内に確保された領域で、値はそこに格納されているビットコンビネーションと捉えましょう。プログラミング言語はたいてい型概念を持つ*1ので、値であるビットコンビネーションには高級な(たとえば整数としての)意味付けがされています。

変数は入れ物(実体は連続したメモリセル)なので先頭アドレスを考えることができますが、値自体にアドレスは考えにくい; 例えば整数変数xに値3が格納されているとき、xのアドレスは取れますが、3のアドレスって意味不明。-- と、そんなことを言ったりもします。が、これもアヤシイよね。

整数値3は、それ自身でアイデンティティを持つので、3が4に変わったりはしない(変わった結果はもはや3じゃない)のですが、「3をインクリメントしたら4」とか平気でみんな言います。「インクリメントする」の目的語は変数であって、ほんとは値じゃないはずです。x++ は意味を持ちますが、3++ は意味を持たないのが普通です。でも、increment(x) = x + 1 という関数を定義して、それの後置演算子形式として'++'を使うなら、3++ は意味を持つし、文字通り「3をインクリメントしたら4」ですよね*2

値自体を表現する整数リテラル「3」のアドレスは考えにくいと言いましたが、文字列リテラル"hello"のアドレスはときに考える必要があります。同じ綴りの2つの文字列が“等しい”かどうか? なんて問題は、リテラルの格納位置を抜きには考えにくいです。

混乱しましたか? 言葉の定義や使用法には曖昧性や文脈依存性が付きまとうのでしょうがないのです。それぞれの文化習慣や文脈ごとに、正確な意味を理解した上で、実際にはエエカゲンに使う、ということになります。



今までは、変数はメモリ内にあるという仮定で話てきました。メモリとは無関係な変数概念もあります。


#define max(x, y) ((x)>=(y)? (x) : (y))

これはC言語のマクロですが、maxの定義に出てくる引数変数x, yは、単にテキストとして存在するだけで、実行時メモリ内に棲んでいるわけではありません。

似たような、けど異なる分野の例として、次のテンプレートを考えてみます。


こんにちは、{$member}さん。

$memberは変数ですが、テンプレート展開エンジンで別なテキストに置き換えられるだけです。しかし、「変数$member」に対して抱くイメージは、置き換えに使うテキストの実体のほうかも知れません。$memberの値(例えば、"檜山")は、テンプレートテキスト上の存在ではなくて、メモリ上のどっかにあるはずです。「変数」がテキスト上の“穴”を意味するか、穴に埋める値の実体を意味するかは文脈によりけり。

置き換えテキスト処理の対象となる変数(穴)は、プレースホルダーと呼ばれたりもします。一時僕は、「場所取り変数」という訳語を使ってましたが、全然一般的にならないので、今はカタカナ書きを使ってます。



メモリ内に存在する変数はアドレスで識別できますが、人間はソースコード上の名前で識別します。この名前は、実行時には消えてしまうこともあれば、実行時にメモリにちゃんと存在するケースもあります。

C言語のようなコンパイル方式のプログラミング言語*3では、実行時に変数や関数の名前は不要なので、メモリに持ち込まれることはありません。ただし、デバッグ目的では名前がないと人間が辛いので、名前情報をずっと持ち続けます。

リフレクションのような事をやろうと思うと、名前は必須です。Javaはもともとリフレクション機能を持っているので実行時にも名前を持っています。C言語なのに、デバッグ用の名前情報を使ってアクロバティックにリフレクションを実装した例を見たことがありますが、詳細は覚えてません。

スクリプティング言語(これも曖昧な言葉ですが)だと、変数から値を取り出すときに、直接アドレスではなくて名前のテーブルを引く方式が多いですね。これだと、名前がないと変数参照ができないので、当然に名前を保持します。



以上、思いついたことをズラズラ並べただけで、この記事に系統性はないのですが、「技術用語であっても、定義や使用法は曖昧だ」ってことは認識しておくべきです。でも、だからといって曖昧な理解しかしてないのも困るのです。

*1:BCPLは高級言語だけど型概念がなかったですね、たしか。

*2:追記:変数のインクリメントは状態遷移です。アイデンティティを持つオブジェクトが別な状態になっただけのこと。一方、increment関数を考えるときは、関数適用、つまり計算した結果を出力していることになります、状態を持つオブジェクトは登場していません。

*3:コンパイル方式かどうかは、プログラミング言語の特性というよりは処理系の特性です。また、「コンパイル方式」の定義は曖昧で、なんとも言えない例があります。