レキシカルとダイナミック:2種類の入れ子

変数のように、あるLisp Objectを名前で参照するときに、2つの入れ子構造を使い分けます。これらをレキシカル・スコープとダイナミック・スコープと呼びます。

スコープとは、ある名前(シンボル)とLisp Objectの結びつき(バインディング、binding)が見える(有効である)ソースコードの範囲です。

入れ子の内側にいて、ある名前の値を参照しようとすると、その名前の一番内側のバインディングが見えるようになっています。同じ名前の外側のバインディングは一時的に見えなく(shadowed)なります。

レキシカル・スコープ
レキシカルな、つまり、プログラムのソース・コードの字面上でのオペレータの入れ子構造によって範囲(スコープ)が表されます。

KISS>(defun f (a)                              ;; ----------+
       ;; a is an argument given when called   ;;           |
       (let ((result (list a)))                ;;           |
         (let ((a 0))                          ;;           |
           ;; a is 0                           ;; -------+  |
           (setq result (cons a result))       ;;        |  |
           (let ((a 10))                       ;;        |  |
             ;; a is 10                        ;; ----+  |  |
             (setq result (cons a result))     ;;     |  |  |
             (let ((a 100))                    ;;     |  |  |
               ;; a is 100                     ;; --+ |  |  |
               (setq result (cons a result)))  ;; --+ |  |  |
             ;; a is back to 10                ;;     |  |  |
             (setq result (cons a result)))    ;; ----+  |  |
           ;; a is back to 0                   ;;        |  |
           (setq result (cons a result)))      ;; -------+  |
         ;; a is back to the original argument ;;           |
         (setq result (cons a result))         ;;           |
         result))                              ;; ----------+
f

KISS>(f -1)
(-1 0 10 100 10 0 -1)

KISS>
ダイナミック・スコープ
ダイナミックな、つまり、実行時のオペレータ(関数やスペシャル・オペレータ)の動的な呼び出し関係の入れ子構造によってバインディングが見える範囲(スコープ)が決定します。

KISS>(defun foo (x)
       (dynamic-let ((y x))
         (bar 1)))
foo ;; 関数foo定義完了
                
KISS>(defun bar (x)
       (+ x (dynamic y)))
bar ;; 関数bar定義完了

                
KISS>(foo 2)
;;(foo 2)により関数fooが呼び出される
;;  (foo 2)
;;    |  関数fooの引数であるレキシカル変数xは2
;;    |  関数foo内の(dynamic-let ((y x))で、xはいま2なので、
;;    |  ダイナミック変数yの値は2に設定される
;;    |  (dynamic-let ((y 2))
;;    |
;;    |    関数foo内の (bar 1) により関数barが呼び出される
;;    |    (bar 1)
;;  |      |関数barの引数であるレキシカル変数xは1
;;    |      |
;;    |      |関数bar内の (dynamic y) は
;;    |      |実行中の関数foo内の(dynamic-let ((y 2))による
;;    |      |ダイナミック変数yを参照
;;    |      |つまり(dynamic y)は2となる
;;    |      |
;;    |    (+ x (dynamic y)) は、(+ 1 2)になる
;;    |    3を返す
;;    |
;;    3を返す

3

KISS>




Lispのリスト図解

Lispが扱うリストの構造を図解します。これは理解のためのイメージですので、必ずしも実装がこの図解のままの構造を使っているとは限りません。

リスト(正式にはproper list)のテキスト表現は要素をスペースで区切って並べてそれをカッコ()でくくります。

例えばリスト

(a 1 2.0)

は、最初の要素はシンボルaです。2番目の要素は整数1、3番目の要素は小数2.0です。

このリストの処理系内での構造を図にすると次のようになります。

 (a            1           2.0)

+---+---+    +---+---+    +---+---+    +---+
|CAR|CDR---->|CAR|CDR---->|CAR|CDR---> |nil|
+-|-+---+    +-|-+---+    +-|-----+    +---+
  |            |            |
  V            V            V
+---+        +---+        +---+
| a |        | 1 |        |2.0|
+---+        +---+        +---+

リストは、Consセルと呼ばれる部品から作られています。
Consセルは、CAR、CDRという名前のポインタを持ちます。
リストの要素は必ずCARが指し、CDRはリスト構造のために使います。

+---+---+   +---------+
|CAR|CDR--> | Object2 |
+-|-+---+   +---------+
  V
+---------+
| Object1 |
+---------+
(a 1) なら、

+---+---+    +---+---+    +---+
|CAR|CDR---->|CAR|CDR---->|nil|
+-|-+---+    +-|-+---+    +---+
  |            |            
  V            V
+---+        +---+        
| a |        | 1 |        
+---+        +---+       
(a) なら、

+---+---+    +---+
|CAR|CDR---->|nil|
+-|-+---+    +---+    
  |
  V          
+---+        
| a |        
+---+        
() なら、

+---+
|nil|
+---+

シンボルnilは、リスト構造の終わりを表す印として使われますが、要素が何もない空リストは、この印nilのみで表すのが自然です。そこで、シンボルnilはシンボルでありながら同時にリストでもあるという面白い性質を帯びることになります。

KISS>(symbolp 'nil) ;; nilはシンボルか?
t                   ;; yes

KISS>(listp 'nil)   ;; nilはリストか?
t                   ;; yes

KISS>(eq 'nil '())   ;; シンボルnil は 空リスト()と同一物か?
t                    ;; yes

KISS>(length '())    ;; ()の要素数は?
0                    ;; 空リストは要素を持たない

KISS>




陰謀論サイトを読んで思ったこと

いわゆる、「ロスチャイルド家とかロックフェラー家が~」っていう感じの陰謀論のサイトを読んでみた感想を少し書きます。

正直、すこし面白いなと思いました。もともと、UFOとか超能力とかその手のオカルトや陰謀論の話題が好きなんですね。中学生の頃は、仲間と一緒に「ムー」を回し読みしていました。

たしかに、自分が大統領と家族ぐるみの付き合いがあるとか、金融界の大物とたいがい顔見知りだったりという状況にあったら、また自分自身もそうとうな金持ちであるとしたら、なんらかの影響力つまりパワーを持っているという自覚は必要でしょうし、パワーを持っている以上、それをどう使うのか、また、使わないのかは判断するべきだと思います。

で、必然的に、その人は世界におけるリーダー的役割を務めざるを得ないと思うのですが、そういう人に直接会ってみて、オフレコで、ぶっちゃけ世界をどうしたいのか聞いてみたいです。自分の影響力(パワー)をできるだけ維持したいとかでもいいので、ホンネを聞いてみたいです。

ネットで読んだ話ですが、世界のトップ10人の金持ちが持っている資産の合計=全世界の貧乏人にあたる下半分(35億人くらい?)の資産の合計だそうです。

いわゆる格差が広がっていると。で、歴史上を見てみると、こういう格差が解消されるのは、常に広い意味での暴力(革命・戦争・疫病の大流行などなど、つまり大量の血が流れる)によっているということを、これもネットでどっかの偉い教授が言っているのをみました。

う~ん、どうも、あまりパワーを集めるのを追及すると、いつかそれってひっくり返されるんじゃないかって恐怖心はわかないのかな…

私ももう少し金パワーが欲しいな(笑)…

などなど、いい妄想のタネになりました。




Lispのオペレータは関数とマクロに大別できる

REPループについての説明で言ったように、Lispにコンピュータ的な処理をさせるには、要素をカッコで囲んだリストを入力してやります。

(OPERATOR arg1 arg2 ...)

そのリストの最初の要素には普通、オペレータ(を表すシンボル)がきます。

処理系が、このオペレータに渡す引数の扱いのちがいで、オペレータは関数とマクロに大別できます。

関数
すべての引数を一度、評価(eval)してから、関数本体に渡す。
KISS>(+ 10 *pi*)
;; シンボル+が表す関数本体には、
;; 引数として 10を評価した結果 10 と
;; 定数*pi*を評価した結果 0.3141592653589793e1 が渡される

0.13141592653589793e2 ;; 10 + 3.14 = 13.14

KISS>
KISS>(* (+ 10 *pi*) 3)
;; 最初に、掛け算をする関数*への引数がすべて評価される
;;
;; 一つ目の引数 (+ 10 *pi*) を評価すると上記のように値
;; 0.13141592653589793e2 が得られる。
;;
;; 二つ目の引数 3 は評価しても 3 のまま
;;
;; したがって、関数*の本体は、引数として
;; 0.13141592653589793e2 と 3 を受け取り、
;; 掛け算をして結果を返す

0.39424777960769379e2
;; 3.14 + 10 で 13.14 、13.14 * 3 = 39.42
KISS>

定義用オペレータ(defining operator)であるdefunを使ってユーザは関数を定義できます。

マクロ
すべての引数を、そのまま、マクロ本体に渡す。

なぜ、マクロがあるかと言うと、if, whileなどのような構文をユーザが自由に定義できるようにするためです。
たとえば、if構文は以下のような見た目をしています。

(if test-form then-form [else-form])

あるLisp Objectを評価(eval)されるものとして見るとき、それをフォーム(form)と呼びます。

KISS>(if (> 10 0) 'plus 'minus-or-zero)

plus

;; test-formである(> 10 0)を評価すると真(t)なので
;; then-form である'plus が評価されplusが返された
;; else-form である'minus-or-zero は一度も評価されないまま

KISS>

私が実装中のISLisp処理系KISSでは、スペシャルオペレータ if は次のように実装されています。
スペシャルオペレータは、あらかじめ用意されたオペレータで、引数の扱いから見るとマクロのように見えます。

/* special operator: (if test-form then-form [else-form]) -> <object> */
kiss_obj* kiss_if(kiss_obj* test_form, kiss_obj* then_form, kiss_obj* rest) 
{
  //test_form を評価(kiss_eval)した結果が KISS_NIL か
    //どうかで、then_form か rest の
    //どちらか一方だけが、
    //評価(kiss_eval)される
    if (kiss_eval(test_form) != KISS_NIL) {
	return kiss_eval(then_form);
    } else {
	return kiss_eval_body(rest);
    }
}

定義用オペレータ(defining operator)であるdefmacroを使ってユーザはマクロを定義できますが、どの引数が評価されるかは、自由に決められます。




Lispへの入力はどんな種類があるのか

Lispへの入力、つまり、REPループへの入力にはどんな種類があるのか、またそれが評価(eval)されるとどうなるのか、ざっくり説明させていただきます。

・Self evaluating objects (数字、文字列など) => そのまま
KISS>1

1  ;; self evaluating object つまり評価(eval)されても
   ;; 自分自身のまま

KISS>
KISS>3.2

0.32e1  ;; 3.2 = 0.32e1 数値としては同じ
        ;; REPループにおけるprint関数がどう
        ;; 表示するかの問題

KISS>
KISS>"been there, done that"

"been there, done that"  ;; 文字列も評価(eval)されても
                         ;; 同じです

KISS>
・シンボル => 変数や定数の参照
KISS>*pi*            ;; "*pi*"という名前のシンボル

0.3141592653589793e1 ;; 定数*pi*には円周率πの値が
                     ;; 設定されている

KISS>

ここで、*pi* というのが”*pi*”という名前のシンボルを表していおり、シンボルを評価(eval)すると、そのシンボルと結び付けられているLisp Objectを返すことになっています。(Cなどのプログラミング言語の定数や変数に相当)。

Lispでは、変数、関数、クラスなど各種Lisp Objectの名前を表すのにシンボルを使います。シンボルは処理系の実装でオブジェクトとして実体をもつものとして定義されることが多いのですが、その説明は別の機会に譲ります。

・リスト => 各種処理
KISS>(+ 1 2 3)  ;; リストの第一要素(ここでは+)によって
                ;; 色々起こる

6 ;; 1 + 2 + 3 = 6 ここでは+は引数をすべて足し算している

KISS>
KISS>(defun hello-world () "hello world!")  
     ;; hello-world関数を定義

hello-world ;; 関数定義が完了すると関数名のシンボルが返される

KISS>(hello-world) ;; 関数を呼び出してみる

"hello world!"

KISS>
KISS>(quote love) ;; quote はスペシャルオペレータと
                  ;; 呼ばれるもので関数とは引数の扱
                  ;; いが異なる

love              ;; quoteは1つのLisp Objectを受け取り、
                  ;; それをそのまま返す
                  ;; ここではシンボルlove。

KISS>'love        ;; quoteはよく使うので省略記法が
                  ;; 用意されている

love           ;; 'love ≡ (quote love)

KISS>love         ;; じゃ、quoteしないとどうなるか…

KISS| Unbound variable love

                  ;; 変数loveに値を設定していないのでエラー
KISS>




LispのREPループとは何なのか

Lispを触り始めると、まずいわゆるREPループと対面することになると思います。

簡単に言うと、Lispのコマンドラインみたいなものです。

で、そのコマンドラインがLisp的にどういう風に実現されているかという観点から命名されたのがREPループです(Read -> Eval -> Print ループ)。

                         Lispユーザー   
    
       Textual Representation   Printed Representation
                |                           ^
                |                           |
    ~~ 端末を使ってすべて文字列でユーザーとやりとりします ~~~
                |                           |
      +---------|---------------------------|--------+
      |         |       Lisp World         |        |
      |         V                           |        |
      |   +----------+  +-------- -+  +-----------+  |
      |   |   read   |  |   eval   |  |   print   |  |
      |   +-----+----+  +-------+--+  +-----------+  |
      |         |         ^     |           ^        |
      |         V         |     |           |        |
      |    Lisp Object----+     +-> Lisp Object      |
      |                                              |
      +----------------------------------------------+
  • read関数が入力としてユーザーが打った文字列(textual representation)を読み、その入力文字列の内容で表現されているLisp Objectを作成して出力する。
  • そのLisp Objectをeval関数が”評価(evaluate)”してその結果のLisp Objectを出力する。
  • そのLisp Objectをprint関数が文字列(printed representation)に変換して出力する。

たとえば、(+ 1 2) とrepループに打ち込んだら、次のような処理の流れになります。

       
           "(+ 1 2)"                       "3"
                |                           ^
      +---------|---------------------------|--------+
      |         |       Lisp World         |        |
      |         V                           |        |
      |   +----------+  +-------- -+  +-----------+  |
      |   |   read   |  |   eval   |  |   print   |  |
      |   +-----+----+  +-------+--+  +-----------+  |
      |         |         ^     |           ^        |
      |         V         |     |           |        |
      |       (+ 1 2) ----+     +-> 3 ------+        |
      |                                              |
      +----------------------------------------------+

また、たとえば単独の数字を入力しても”1″ -> read -> 1 -> eval -> 1 -> print -> “1” のように、数字を評価(evaluate)しても元のLisp Objectとしての数字から変化しません。(eval 1) -> 1。単純で分かりやすいですね。

では、コンピュータ的な各種処理を行わせるにはどうするかというと、それはリストを入力してあげることで実現しています。

(OPERATOR arg1 arg2 ...)

リストを評価(evaluate)するときは、上記のように、第一要素を演算子(オペレーター)として扱い、第二要素以降はその演算子への引数として渡すというルールになっているのです。

つまり、プログラムをリストとして入力することになります。
LISP = LISt Processor という名前の由来です。




全知全能の神はいるのか

まぁ、全知全能の神がいるとします。すると矛盾がでてくるんですね。

全能の逆説というらしいです。

全知全能の神が、

「どんな矛も防ぐ盾を出すよ、ほぃっ」

スポポ~ン 無敵の盾登場

「どんな盾も貫く矛をだすよ、ほぃっ」

スポポ~ン 無敵の矛登場

市民1「じゃぁ、神様、その矛でその盾を突いてみたらどうなりますか?」

これは矛盾ですが、私はいままで50年近く生きてきて矛盾を抱えていない人って見たことないんですが、それは置いといて、たとえば、無敵の矛で無敵の盾を突いたとします。ちょっと思いつくのは、

”矛盾なので世界そのものが消える”

こんなんどうでしょう、結果が出ないというのが正解のひとつではないでしょうか?

神様「あれ、面白くないねぇ~、じゃ、無敵の盾と無敵の矛を対決させても、世界が消えないようになぁ~れ!!ほぃっっと」

そしてまた、無敵の矛と無敵の盾を対決させます…

市民A「いくぞ~、そりゃ!」

”こんどは、世界は消えないが、時間が永遠に止まる”

などなど…

複雑系のこととかをちょっと読んだり、量子力学の二重スリット実験のことを読んだりして、いかに矛盾を内包した系を説明するかって大切なんだろうなぁと思っています。

 




ISLispHyperDraftのタグ付けがだいたい終わりました

ISLispHyperDraftとは、プログラミング言語Lispの国際規格であるISLispの規格草案をHTML化したものです。

作業を始めてから、みなさんからのサポートや助言をいただきながら、すべての章のタグ付けが終わりました。いろいろとお世話様でした。

これからも、手を入れていく予定ですが、最初に私が望んだ見た目にはなったので、とりあえずは一段落したという感じです。

github.ioで閲覧できます。(リポジトリ)

それではみなさんハッピー・ハッキング!


ISLispHyperDraft is an html version of ISLisp specification draft.

ISLisp is a programming language Lisp standardized by ISO.

Almost all the tagging is done now.

I still intend to touch up here and there but for the time being, this is it.

You can browse it at github.io. (the repository is here)

Happy hacking!




この世のすべては虚妄である

たとえば、私のお姉ちゃんの例を出しますと、旅先で魚料理をおいしく食べたそうです。で、食後に旅館の人と話していて、さっき食べた魚が実はフナだったと分かっとたんに、気分が悪くなってしまったそうです(姉はフナを食べるのは気持ち悪く感じられて嫌いだそうです)。

「あぁ、さっきの魚はフナですよ」

この言葉が、姉の脳で解釈された結果、身体に異常を生じさせるほどの威力を発揮したのです。

他の例では、最近は映像の分野としてスプラッターものがあります。

で、思考実験ですが、「スプラッター風味のスリラー映像です」ということわりを最初にした後で少しグロい殺人の場面がある映像を被験者に視聴していただきます。

で、その映像を”楽しんだ”人たちが一定数いるとします。

その人たちに、「申し訳ないですけど、あれ、本当にあった殺人事件が偶然録画されたものなんです」といったら、やっぱりその人たちは激しい拒絶反応を起こすのではないでしょうか。

「ホントウとウソの区別は本当にそんなに明確なものですか?」

私のお気に入りのお釈迦様は、「この世のすべては虚妄である」と断言しています。

とはいうものの、私が死んだ後…

「じゃ~ん、今のは最新体験型のゲームだったんですけど、いかがでした?」

とあの世で言われたら、さすがの私もその人を殴ってしまうかもしれません(笑)

「数式ということばじゃないと正しく伝わらない」問題

趣味で、初期仏典や聖書、コーラン、ヨガのギータなんかを乱読しているんですが、やはり自然言語によっていかに良いことを伝えようとしても、千年もすると(故意かどうかは微妙ですが)すっかり内容と逆のことを読み手が勝手にやりだすという事例を多く見聞きします。

たとえば、釈迦は「異性関係は人間の生殖にかかわる本質的な欲望なので、むやみに淫らにならないように、(男性の弟子が多かったので)女性には気を付けろ」というようなことを折にふれて語っていたり、「肉を食うからなまぐさと言うのではない、友人のカミさんと陰で一発やってそ知らぬふりをしているのを生臭というのである」なんて言ったりしています。

これが数千年たって、仏教がインドから中国、さらに日本へとくると何が起こったかと言うと…

「異性関係は気を付ける」→「女はやばいらしい」→「女とヤるのはよそう」→「男とヤればいい」

なんでだよ(笑)wwwwwwwwwww

たとえば、日本の寺関係の隠語で「幼い小僧」への性的表現がかなりあるとか、仏教僧を紹介する書物で、「この僧は女性との性関係をもたなかった、さらに男性との性関係ももたなかった」ということが”珍しい人”として紹介されていたり。

またキリスト教でもアメリカでカソリックの神父による幼児への性的虐待が社会問題になっていてバチカンはたしか総額数千億レベルの訴訟を抱えていると聞きました。

残念ながら、釈迦、イエス・キリスト、ムハンマドなど名だたるセイント(聖人)たちは、失敗したと言わざるを得ません。

釈迦だって、大悟をしたあと一度はそのまま死ぬ気だったのですが、まぁ、神話的表現にあえてしてますが、梵天に請われて世に「我は仏陀である」と大見得をきって宣言して、「人々の苦を滅しよう」という意図を持ったはずです。

だいたい、釈迦などがいう煩悩のコントロールは、よく釈迦本人も「激流を泳いで渡るほど難しい」と自分で言っていますし、「煩悩の激流を渡りきるものは数少ない」と言っています。

しかし、いったん社会的リーダーになろうと決意したのなら、所定の訓練をつめば、ほぼ九割以上の人が煩悩のコントロールができるようになるような訓練メソッドを残して、その結果、人類が戦争をしなくなったというような”結果”が欲しかったところです。

「激流渡り水泳スクール」っていって生徒を集めていて、激流を渡れるようになるのはほんの数人っていうのは許されないと思います。

みなさんにおなじみの日本の仏教者で一休さんという方がおられました。

その一休さんは、次のような言葉を残しておられます。

「釈迦といふ いたづらものが世にいでて おほくの人をまよはすかな」

まさに、私の感想もこの句につきます。

なぜなら、インドでは今、ヒンドゥーが支配的な宗教ですが(不可触民に仏教を広めている日本人の方がおられますが)、じゃ、仏教徒はどうなったのかというと、釈迦の没後数百年に、ヒンドゥー教徒による仏教徒の虐殺が起こっているんですね。また、最近ではミャンマーの仏教徒からのロヒンギャへのひどい虐待の問題なんかもあります。

「お釈迦様、あんた、問題解決するどころか、ぶっちゃけ問題を増やしたんじゃないんか?」


これらセイントの失敗のひとつは、聖典が情緒的な自然言語によって記されたことが原因の一つだと思います。

そこへいくと、数学のたとえばユークリッド幾何学なんて数千年たってますが、根底から覆されることはありませんでした。リーマン幾何学などの前提条件を変えたさらなる発展はあり、それはそれで実り多い発展をしましたが、いまだにユークリッド幾何学はほぼ万人に正しく解釈、理解されてその正しさに挑戦して成功した人はおりません。

また、数学はゲーデルの不完全性定理により、己の限界さえもきっちりと示しています。

いま私はこれらの、「数式もしくは形式言語でなにか言わないと、バカが曲解する問題」をちらほらと折にふれて考えています。