Posted 2016-12-01 15:00:59 GMT

レトロLisp アドベントカレンダー 第2回目は、LISP 1 についてです。

LISP 1の次のLISP 1.5がとても有名なためLISP 1なんてあったのかという感じですが、1.5があるのだから当然1はあるわけなのです。

しかし、LISP 1は、どの辺りからどの辺りまでが該当するのか判然としません。

1962年のLISP 1.5 マニュアルの発表がLISP 1.5の成立と考えられるので、大体それ以前、という所でしょうか。

方言の系統

LISP 1

主要開発者

John McCarthy、Steve Russell

登場時期

1958

概要

Programs with Common Sense という論文を発表し、Advice takerという常識データベースを元に推論し利用者に助言を与えるというシステムを構想していたマッカーシー先生は、前回探検したFLPLの知見を活かし独自の人工知能向け言語を模索し始めます。

言語に盛り込みたい機能は、

関数 以前から考えていた条件式(マッカーシー条件式/所謂LISPの COND ) 条件式を利用した再帰関数定義 リストのマップ関数(MAPLIST、Common LispでいうMAPCARの先祖) 関数引数の為の表記。λ記法(しかし、λ計算については、表記以上のものは導入せず(当時λ計算についてあまり理解していなかった為)) 手動でリスト構造を削除すること(メモリ管理)からの脱却

位のところだったようです。

1958年秋、マッカーシー先生は、MITにて、人工知能プロジェクトを始動させます。

同時にLISPの実装も開始されたため、1958年秋がLISPの誕生とされることが多いようですが、History of Lispに当時の記録がありますが、ここでは、MITのAIメモを順番に眺めてみましょう。

AIM-1: An Algebraic Language for the Manipulation of Symbolic Expressions

まだ、M式もない様子。

FLPLと同じく、IBM 704のワードの構造に密着していて、その結果、 car 、 cdr 以外にも、 c.r があります。

その種類は、

p the prefix (bits s,. 1, 2)

i the indicator (bits 1 and 2)

s the sign bit

d the decrement (bits 3-17)

t the tag (bits 18-20)

a the address (bits 21-35)

となっていて、 cpr 、 cir ……等々が存在します。

ワード全体を取得する、 cwr もあり、それぞれ専用のアクセサがあるので、 add(cwr(J)) で、 car(J) と同じ働きをしたりするようです。

値の格納のほうもあって、 stwr 、 stpr 、という風に、 st +一文字 r になっています。

rpalca より star 、 stdar の方がよっぽど良さそうですが、格納の方は継承されなかったんですね。

他の機能も大体FLPLに倣った感じがありますが、リストの作成は、 consw でメモリを確保して、 comb で組になる値をセット、 erase で消去、という感じです。

この consなんとか + comb が後の cons の先祖にあたりますが、だいぶごちゃついています。

ちなみに、FLPLでは、LWORDでワードを確保しますが、 consw 、 stwr がそれにあたると思われます。

コード例

この頃のコード例が載っていますが、 prog はまだ存在しないものの繰り返しで go を使うのはほぼそのままです。

/ J = 0 → return go (a (cir(J)) a(1) jnk = erase (car (J)) a(O) eralis (dec(erase(J))) return a(2) eralis (car (J)) \ go (a(o))

当初からLISPには繰り返し構文があったとマッカーシー先生も書いていますが、「 LISP も実用主義に囚われて go to が導入されるようになった」説は、後世の人が作ったお話の一つのようです。

また、さらに、FORTRANの“do”のような繰り返し構文を入れることも検討したりしていたようです(後のMACLISPでは実際に do として導入)。

上記は繰り返しでしたが、再帰を使う例も載っていて、

function copy (J) / copy = (J = 0 → 0, 1 → consw (comb 4(cpr (J), copy(cdr (J)), ctr(J), (cir(J) = 0 → car (J), cir(J) = 1 → consw(cwr(car(J))), cir(J) = 2 → copy(car(J)))))) \ return function diff(J) / diff = (ctr(J) = 1 → consel(0, 0), car(J) = "x" → consel(1, 0), car(J) = "plus" → consel("plus", maplist(cdr(J), K, diff(K))), car(J) = "times" → consel("times", maplist(cdr(J), L, (L = K → diff(L), L ≠ K → copy(L))))) \

という風になっています。前M式という感じです。

まず、リストの外部表現としてお馴染の括弧を使うかどうかもどの辺りで決めたのかあやふやとのことですが、ブラケットは導入されていません。

cond が → のあるなしでしか区別できなかったりするのは、M式と同じですがさらに読み難い気がします。

また、M式でシンボルを大文字で T と表現する〈S式の (quote foo) 〉のに対し、 "foo" と書くようです。逆にモダンに見える不思議。

ちなみに、ブール値の true ですが、後のM式では、 T の値は 1 となっていて一応名前が付いていますが、上のコードでは、そのまま 1 となっています。

また、今となっては想像しがたいことですが、再帰はおろか関数の仕組みを実現するのにスタックを利用すること自体がまだ新しい試みという時代だったようです。

マッカーシー条件式も、関数呼び出しも、今では当たり前過ぎて何が革新だったかの評価も難しく感じます。

AIM-2 Symbol Manipulating Language

まだLISPという名前はなかった様子。チャーチのλ記法を取り入れたのと記述が主な所でしょうか。

AIM-3 Symbol Manipulating Language–Memo-3– Revisions of Language (1959-10頃)

さて、ちょっと進化して、AIM-3位の所ですが、リスト操作の関数は、 car 、 cdr 、 cwr 位に整理され、だいぶお馴染の姿になってきました。

また、式の表現が、

(times,x,(plus,x,1),(sin,y))

のように固まってきたようです。 consw + comb のようなものも cons にまとまりました。

AIM-4 Symbol Manipulating Language–Memo-4– Revisions of Language

LISPという名前が登場するのは、AIM-4からのようです。

この辺りからLISP 1といっても良いのかも。

Advice Takerからの知見として属性リストを導入したようですが、属性リストは、シンボルをオブジェクトに見立てたような所もあったらしいです。

Guy Steele Interviews John McCarthy, Father of Lisp

That idea, in some sense is a realization of the idea of an object, which in my mind actually precedes Lisp. In my paper on programs with common sense, I mentioned an object as something that you add something to say about a part from its structure. The example I think that I gave, was the number 1776, which you remember apart from its arithmetic structure because of it being the date of the American Revolution. There were these objects and I gave several examples and then, in Lisp, I thought about having these property lists. They were used differently from the way I had imagined them being used.

AIM-8 Recursive Functions of Symbolic Expressions and Their Computation (1959-03-13)

ちょっと進んで、お馴染のタイトルが見えてきましたが、AIM-8が有名な論文の草稿なんでしょうか。

ブラケットが導入されて、M式という感じになりました。

また、リストでのシンボルは大文字で書くことになり、操作しているS式とメタなM式の区別がつきやすくなりました。

M式とS式の対応についての説明で、

空のS式: Λ (に似た記号) ⇒ NIL

(に似た記号) ⇒ NIL 1 ⇒ T

0 ⇒ F

とあります、ここで0は、NILでもあったので、空リストと偽の値が既に分離していないというのが分かります。

さらに、 first の定義を眺めると、 atom[x] → Λ なので NIL の car は NIL が発生してしまっています(概念としては分離しているのでundefとも考えられますが)

また、論文にまとめることを意識したからなのか、 car 、 cdr 、 cons という名前は、 first 、 rest 、 combine となっています。

LISPあるある話である謎のcar/cdr問題もこの時点で払拭できた筈ですが、利用者が、car/cdrを選択したということなのでしょう。

S式/M式問題にも似たものを感じますが、マッカーシー先生が利用者に特定の名称の利用を強要するような人でなかったとも、そもそも関心がなかったとも考えられます。

M式の見た目については、FORTRANにできるだけ似せるようにした、とのことですが、その割には似てないような。

ともあれ全体的にお馴染のLISP 1.5にかなり近くなりました。

maplist=λ[[x,f];[null[x]→Λ;1→combine[f[x];maplist[rest[x];f]]]]

をSchemeで書くと、

lambda ( x f ) cond ( ( null x ) Λ ) ( 1 ( combine ( f x ) ( maplist ( rest x ) f ) ) ) define maplist

という感じになりますが、そのまま翻訳できるところまで整いました。

また、S式以外にも、Linear Lispという文字列で表現したL-expression、バイナリで表現したBinary Lisp等、S式から派生した謎の概念が紹介されています。

大体この辺りまでが纏められて、少し後のLISP I Programmer's Manualになっているようですが、こちらはほぼLISP 1.5と変わりないものになっているようです。

LISP 1までの反省

LISP 1以前〜LISP 1.5 は大体こんな感じかなと思いますが、マッカーシー先生の反省として、

数もアトムのリストとしたが、とても遅くなった

空リストNILとfalseが実装上で同じくアドレスの0を利用したため混合することとなった

S式版のCONDの括弧が多い

があるようです。

S式版のCONDの括弧が多い、というのは、グループ化の括弧が一つ少ないClojureのM式の cond のようなものを指しているようです。

“,”が空白と同じ扱いというのもそうですが、LISP 1.5のアイデアを正統に引き継いでいる所もあるのかも。

まとめ

前回に引き続き、部分によっては無駄に詳しかったり適当だったりと雑なまとめですが、この辺りのことは、MITのAIメモの極初期のものに色々と記述があります。

それなりに面白いのですが、どうもあまりまとめている人はいない気もします。

竹内先生が以前にShibuya.lispで講演された際に、この辺りのことをお話されたと記憶していますが、残念ながら映像/音声ともに記録はNGだったため残っていません。

以前、この辺りの資料を眺めても全然意味が分からなかったのですが、FLPLをちょっと眺めたら、なんとなく分かる所が増えたような気がします。

今回、IPLは全然確認していませんが、IPL由来のものも多いのかも。

参考文献

■

