sync notes(auto)

This commit is contained in:
Kaz Saita(raspi5) 2024-04-28 14:45:04 +09:00
parent 7dee21406a
commit e77ddb5407
2 changed files with 96 additions and 1 deletions

View file

@ -5,11 +5,13 @@
defvar, defparameterはダイナミックスコープの変数(スペシャル変数)を定義する。 defvar, defparameterはダイナミックスコープの変数(スペシャル変数)を定義する。
letや未定義時のsetfはレキシカルスコープの変数を定義する。 letや未定義時のsetfはレキシカルスコープの変数を定義する。
グローバル変数をレキシカルに作る方法は(少なくとも標準的には)無い。 上記未定義時のsetfは期待した動作をするが、推奨されていない。 グローバル変数をレキシカルに作る方法は(少なくとも標準的には)無い。 上記未定義時のsetfは期待した動作をするが、推奨されていない。
[[20240428125606 ダイナミックスコープ レキシカルスコープ |ダイナミックスコープ レキシカルスコープ]] cf. [[20240428125606 ダイナミックスコープ レキシカルスコープ |ダイナミックスコープ レキシカルスコープ]]
cf. [lispguide.xml](https://google.github.io/styleguide/lispguide.xml?showone=Global_variables_and_constants#Global_variables_and_constants) cf. [lispguide.xml](https://google.github.io/styleguide/lispguide.xml?showone=Global_variables_and_constants#Global_variables_and_constants)
> Common Lisp does not have global lexical variables > Common Lisp does not have global lexical variables
- defvar: 値が定義されていなければ定義する。上書きしようとしても存在している場合はできない(というか無視される)。 - defvar: 値が定義されていなければ定義する。上書きしようとしても存在している場合はできない(というか無視される)。
- defparameter: 定義されているかに関係なく定義する。すでに定義されていたら上書きする。 - defparameter: 定義されているかに関係なく定義する。すでに定義されていたら上書きする。
- defconstant: 定数を定義する。複数回定義しようとしたり、値を変更しようとするとエラーになる。 - defconstant: 定数を定義する。複数回定義しようとしたり、値を変更しようとするとエラーになる。

View file

@ -0,0 +1,93 @@
# 20240428125606 ダイナミックスコープ レキシカルスコープ
#software #word
## とは?
以下、C/C++やjavascriptのような、グローバル変数や、関数の中で変数を定義できて、その2つは変な区別が無い言語を想定している。たとえばpythonだと、`global`宣言をしないと関数内でグローバル変数が参照できない・・・という区別があるし、common lispでは `defvar` や`defparameter`を使わないとグローバル変数が宣言できない・・・という区別がある。 cf. [[20240428122916 setf setq defvar defparameter の違い|setf setq defvar defparameter の違い]] 。こういう言語は、それなりに理由があってそのような区別をしているのだが、話がややこしくなるので一旦忘れる。
プログラムを動かした時、ある場所で、
- どのような変数が定義されているか
- 定義されているとしたら、その値はどうなっているか
を判断するにはどうしたらよいかについて、大きくわけると2つの方法がある。
1. 有効な、いちばん内側で定義されている値を使う。
2. 同じ変数名の宣言、定義、変更などが来たら、実行時、最後にそうなった値を使う。関数に入ろうが、出ようが関係ない。
現代(2024)のプログラミング言語では、1.が当然のように使われているため、1. 以外の方法がある可能性について、あまり考えたりすることが無い。
たとえば、以下のCプログラムで、画面に何が表示されるかを考えてみる。
```c
#include <stdio.h>
// global
int a = 0;
void f2()
{
printf("%d\n", a);
}
void f1()
{
int a = 1;
f2();
}
int main(void)
{
f1();
return 0;
}
```
画面に表示されるのは`0`になる。`f1()`の中で、 `int a`を再度定義しているが、それは `f1()`内部のみで有効であり、呼びだされた関数`f2()`では有効ではない。これがレキシカルスコープ。
ここで、`f1()`で定義した`int a`を`f2()`でも、(この書きかただと)参照できてしまうのが、ダイナミックスコープ。
その場合、画面には`1`が表示される(実際に試すことはできないけど)。
レキシカルスコープの言語では、変数名とその参照先は一意に決まる。実行された状況などに左右されない。
ダイナミックスコープの言語では、変数の値がどうなっているかは、実行された状況に左右される(だからダイナミックスコープという)。
例えば、下記を実行したら、ダイナミックスコープの場合は`2`が表示される。実際は0だけど。
```c
#include <stdio.h>
// global
int a = 0;
void f3()
{
int a = 2; // aを2に設定
}
void f2()
{
printf("%d\n", a); // 2が表示される
}
void f1()
{
int a = 1;
f3();
f2();
}
int main(void)
{
f1();
return 0;
}
```
## 経緯とか
そもそもはレキシカルスコープにしたかったが、初期のLISPではダイナミックスコープになってしまったらしい。じゃあその頃、他の言語はどうだったんだ、とか、別途気になる。
レキシカルスコープがいかに重要なのかとか、どうやったらそれが実現できるのかをレキシカルスコープなschemeを作った人が書いたSICPが詳細に説明しているのは、そういう理由からかもしれない(根拠なし)。cf. [評価の環境モデル(SICP)](https://sicp.iijlab.net/fulltext/x320.html)
ちなみにレキシカルスコープのインタプリタを実装しようとすると、関数呼び出しのとき、別の関数を呼ぶ場合、呼び出し元で定義したローカル変数のかたまり(フレーム)を一旦無効にしてから関数を呼び出す必要があり、関数から戻って来た時にフレームを再度有効にする必要がある。この用途のため、フレーム保存しておく必要があるが、再帰呼び出しなどの時、(末尾再帰でないなら)毎回そのフレームを保存しておく必要があり、メモリ消費量が多くなる。というような、「ダイナミックスコープになった経緯」みたいな話をネットでチョイチョイ見るが、フレームの保存をしていないという、現代の視点から見るとバグっぽい挙動に見えてしまう。
### Refs.
- [変数束縛はLispのもう一つの本質である](http://blog.livedoor.jp/s-koide/archives/1872474.html?ref=head_btn_prev&id=3054770)
- [変数束縛はLispのもう一つの本質である続き](http://blog.livedoor.jp/s-koide/archives/1873931.html)
### 参考にしてはいけない
- とてもわかりにくい [動的スコープ - Wikipedia](https://ja.wikipedia.org/wiki/%E5%8B%95%E7%9A%84%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97)