ATPによるプログラミング Copyright (C) 1990 Manabu Daikoku 1 はじめに  ATPは,オブジェクト指向的なプログラミングができるように設計された言語である.本書の目的は,ATPというプログラミング言語,およびATPのインタプリタについて解説することである. 2 インタプリタの操作 2.1 式の入力  ATPのインタプリタは,プロンプトを出力して,式を読み込んで,その式を評価し,その式の値を出力する,ということを繰り返す(式については第4章参照).ATPのインタプリタのプロンプトは'>'である.式は何行にまたがってもよい.ピリオドとリターンを入力することによって式の入力は終了する. 2.2 起動と終了  ATPのインタプリタ(version 1.0)はPrologで書かれている.したがって,それを使うためにはまずPrologのインタプリタを起動しなければならない.Prologのインタプリタが起動したら,    ?- ['atp.pl']. という質問を入力することによって,ATPのインタプリタをコンサルトする.そののち,    ?- atp. という質問を入力すると,ATPのインタプリタが起動して,    > というプロンプトが出力される.  ATPのインタプリタを終了させたいときは,    >atp <- quit. という式をインタプリタに入力すればよい. 3 オブジェクト 3.1 オブジェクトとは何か  ATPを使うときは,コンピュータの中に存在するものはすべてオブジェクトであると考えることができる.オブジェクトとは,データと,そのデータに対して操作を行なうことのできる実体とをセットにしたものである.  すべてのオブジェクトはなんらかのクラスに属している.たとえば,37とか63.4というオブジェクトはnumberというクラスに属している.クラスは抽象的な存在であり,クラスを具体化したものがオブジェクトである.オブジェクトは,それが属しているクラスのインスタンスであると言われる.たとえば,37や63.4はnumberというクラスのインスタンスである.  本書の中で使われている<クラス名>という記法は,そのクラスの任意のインスタンスを意味している.  オブジェクトを動作させたいときは,そのオブジェクトに対して,メッセージと呼ばれる文字列を送ればよい.オブジェクトは,外部からメッセージを受け取ると,そのメッセージにしたがって自分自身に対して操作を行ない,その結果を外部に返す.オブジェクトが返す結果は,やはりなんらかのオブジェクトである.たとえば,37というオブジェクトに[+,20]というメッセージを送ると,37は,自分自身に20を加算し,その結果として57というオブジェクトを返す.  メッセージを受け取るオブジェクトを,そのメッセージのレシーバと言う.  クラスは,自分に属するインスタンスがメッセージを受け取ったときに行なう動作の手続きを持っている.そのような手続きをメソッドと言う.メソッドは,メッセージのひとつのパターンに対してひとつ存在する.たとえば,numberというクラスはいくつかのメソッドを持っていて,インスタンスが[+,]というパターンのメッセージを受け取ったときに実行されるメソッドもその中のひとつである.. 3.2 標準クラス  ATPのインタプリタの中には,最初からいくつかのクラスが組み込まれている.そのようなクラスを標準クラスと言う.標準クラスには次の9個のものがある. (1) nil (2) number (3) true (4) false (5) block (6) atp (7) prolog (8) dos (9) file  標準クラスについては第5章でさらに詳しく説明する. 3.3 未確認オブジェクト  どのクラスに属しているかということをインタプリタが判断することのできないオブジェクトを未確認オブジェクトと言う. 4 式 4.1 定数  定数は,特定の具体的なオブジェクトを参照するための式である.定数は,    クラス名 :: オブジェクト と書く.たとえば,    ningen :: sachiko tsubame :: yoshiharu number :: 37 prolog :: prolog file :: 'b:cntup.atp' などは定数である.::の前後の空白は,ほとんどの場合,省略することができる.ただし,空白を省略すると::の前後に特殊文字が来る場合は正しく解釈されない.  file以外の標準クラスのオブジェクトを参照する定数については,クラス名を省略することができる.したがって,上の例の3番目と4番目は,    37 prolog と書いてもよい.  定数を評価することによって得られる値は,その定数そのものである.したがって,たとえば,インタプリタに    >ningen :: sachiko. と入力すると,インタプリタは,    ningen :: sachiko と出力する. 4.2 変数名  変数名は式である.変数名を評価させると,それによって参照される変数の内容がその値になる.たとえば,今,kyuuriという変数が存在していて,その内容がnumber :: 78であるとすると,kyuuriという変数名を評価させると,その値はnumber :: 78になる. 4.3 メッセージ式  メッセージ式はオブジェクトにメッセージを送るために使われる式である.メッセージ式は,    式 <− メッセージ と書く.メッセージ式が評価されると,<-の左にある式がまず評価され,その式の値として得られたオブジェクトに<-の右にあるメッセージが送られる.メッセージ式を評価することによって得られる値は,メッセージを受け取ったオブジェクトが返した結果を意味する定数である.たとえば,    34 <- [+,13] という式をインタプリタに入力すると,number :: 34というオブジェクトに[+,number :: 13]というメッセージが送られ,number :: 34はnumber :: 47というオブジェクトを返す.したがって,インタプリタは,その式の値としてnumber :: 47という定数を出力する.  メッセージ式の中にさらにメッセージ式を書いてもよい.たとえば,    7 <- [*,4] <- [-,3] というようなメッセージ式を書くことができる.この式を評価させると,まずnumber :: 7に[*,number :: 4]が送られ,次にその結果であるnumber :: 28に[-,number :: 3]が送られ,その結果であるnumber :: 25が値になる.  メッセージ式の中に書かれたメッセージが[式,式,・・・,式]というようなリストであるとき,そのメッセージは,その中の式がすべて評価されたのちにオブジェクトに送られる.したがって,たとえば,    6 <- [*,3 <- [+,5]] という式を評価させると,[*,number :: 8]というメッセージがnumber :: 6に送られる. 4.4 代入式  代入式は,変数にオブジェクトを代入するために使われる式である.代入式は,    変数名 := 式 と書く.代入式を評価させると,:=の左に書かれた変数名によって参照される変数に,:=の右に書かれた式の値が代入される.たとえば,    kabocha := 48. という式を評価させると,kabochaという変数にnumber :: 48というオブジェクトが代入される.したがって,そののち,kabochaという変数名を評価させると,kabochaの内容であるnumber :: 48がその値になる.  代入式の値は,その代入式の評価によって変数に代入されるオブジェクトである. 4.5 quote式  quote式は,    quote(式) と書く.quote式を評価させると,quote式の中に書かれた式がその値になる.たとえば,    quote(daikon) の値はdaikonである.  quote式は,式を評価させたくないときに使われる式である.たとえば,tamanegiという文字列を出力したいとする.    atp <- [print,tamanegi] というように,atpにメッセージを送れば,atpはtamanegiという式の値を出力する.したがって,もしもtamanegiという名前の変数が存在しなければtamanegiという文字列が出力されるが,そのような変数が存在している場合は,その変数の内容が出力されてしまう.したがって,文字列を出力したいときは,    atp <- [print,quote(tamanegi)] というようにquote式を使うほうがより安全である. 4.6 式ではないものの評価  ATPのインタプリタに式以外のものを評価させると,インタプリタは,その式そのものをその式の値にする.その場合,その式の値は未確認オブジェクトとして扱われる. 5 標準クラス 5.1 nil  nilのインスタンスは,nilというオブジェクトのみである.nil :: nil以外のオブジェクトは,自分が理解できないメッセージを受け取った場合はエラーメッセージを出力するが,nil :: nilにどのようなメッセージを送っても,nil :: nilは自分自身を返すだけでエラーメッセージは出力しない. 5.2 number 5.2.1 概要  numberのインスタンスはなんらかの数である.numberのインスタンスは自分自身に対して加算,減算,乗算,除算などの演算,および自分自身と他のnumberのインスタンスとの間の大小の比較を行なうことができる.さらに,自分自身を回数として,なんらかの動作の繰り返しを行なうことができる. 5.2.2 算術演算 [+,]  レシーバにを加算したものを返す. [−,]  レシーバからを減算したものを返す. [*,]  レシーバにを乗算したものを返す. [/,]  レシーバをで除算したものを返す. [//,]  レシーバをで整数除算したものを返す. [mod,]  レシーバをで整数除算したときの余りを返す. [^,]  レシーバの乗を返す. 5.2.3 関係演算 [>,]  レシーバがよりも大きいならばtrue :: trueを,そうでなければfalse :: falseを返す. [<,]  レシーバがよりも小さいならばtrue :: trueを,そうでなければfalse :: falseを返す. [>=,]  レシーバがよりも大きいかまたは等しいならばtrue :: trueを,そうでなければfalse :: falseを返す. [=<,]  レシーバがよりも小さいかまたは等しいならばtrue :: trueを,そうでなければfalse :: falseを返す. [=:=,]  レシーバととが等しいならばtrue :: trueを,そうでなければfalse :: falseを返す. [=¥=,]  レシーバととが等しくないならばtrue :: trueを,そうでなければfalse :: falseを返す. 5.2.4 繰り返し [times_repeat,]  レシーバ回だけ,にvalueを送る.つまり,ブロックの実行をレシーバ回だけ繰り返す.レシーバはtrue :: trueを返す. (例)    >4 <- [times_repeat,[block,atp <- [print,quote(tanpopo)], atp <- nl]]. tanpopo tanpopo tanpopo tanpopo nil :: nil 5.3 trueとfalse 5.3.1 概要  trueのインスタンスはtrueというオブジェクトのみ,falseのインスタンスはfalseというオブジェクトのみである.trueは真理値の真,falseは真理値の偽を意味するオブジェクトである.numberのインスタンスは,大小の比較の結果として,trueまたはfalseを返す.  trueとfalseは,動作の選択や動作の繰り返しを行ないたい場合にも使われる. 5.3.2 論理演算 [or,]  つねにtrueを返す. [or,]  レシーバ自身を返す. [and,]  レシーバ自身を返す. [and,]  つねにfalseを返す. not  レシーバがtrueならばfalseを,falseならばtrueを返す. 5.3.3 ブロックの選択 [if_true,1,if_false,2]  レシーバがtrueならば1を,falseならば2を返す. (例)    >38 <- [>,27] <- [if_true,[block,quote(yes)], if_false,[block,quote(no)]] <- value. yes [if_true,]  レシーバがtrueならばを,falseならばnil :: nilを返す. [if_false,]  レシーバがtrueならばnil :: nilを,falseならばを返す. 5.4 block 5.4.1 概要  blockのインスタンスをブロックと言う.ブロックは,    [block,式,式,・・・,式] と書く.  メソッドを定義するとき,動作の選択や繰り返しを行なうときなどには,ブロックが必要になる. 5.4.2 ブロックの実行  ブロックは,自分自身の中にある式を,先頭から順番に一つずつ評価することができる.そのことを,ブロックを実行すると言う.ブロックを実行させたいときは,そのブロックにvalueというメッセージを送ればよい.ブロックは,valueを受け取ると,自分自身を実行して,最後の式の値を返す. (例)    >[block, atp <- [print,quote(tsutsuji)], atp <- nl, atp <- [print,quote(ajisai)], atp <- nl ] <- value. tsutsuji ajisai nil :: nil >[block,27,38,66,41,71] <- value. number :: 71 5.4.3 繰り返し  何らかの条件について,それが成り立っている間だけ,あるいは成り立っていない間だけ,何かの実行を繰り返す,と言うことを行なわせたいときは,valueを送るとtrue :: trueまたはfalse :: falseを返すようなブロックに,    [while_true,] または    [while_false,] というメッセージを送ればよい.    [while_true,] というメッセージを受け取ったブロックは,自分自身にvalueを送り,にvalueを送るということを繰り返す.ただし,自分自身が返した結果がfalse :: falseである場合は終了する.  第6.2節に,while_trueを使った具体的な例がある. 5.5 atp,prolog,dos 5.5.1 概要  atp,prolog,dosのそれぞれのクラスのインスタンスは,atp,prolog,dosというオブジェクトである.atpはATPのインタプリタであり,prologはPrologのインタプリタであり,dosはMS-DOSである. 5.5.2 atp  atpが持っているメソッドは次の通りである. quit  終了する.つまり,Prologのインタプリタに戻る.したがって,ATPのインタプリタを終了させたいときは,    >atp <- quit. と,インタプリタに入力すればよい. listing  メモりーにあるプログラムを画面に出力する. [write,オブジェクト]  オブジェクトを出力する.ただしクラス名は出力されない.未確認オブジェクトは出力できない. [print,オブジェクト]  オブジェクトを,    クラス名 :: オブジェクト という形式で出力する.未確認オブジェクトも出力することができる.未確認オブジェクトの場合はクラス名は出力されない. [printq,オブジェクト]  [print,オブジェクト]とほとんど同じであるが,オブジェクトの中のシングル・クォーテーション・マークも出力する. nl  改行を出力する.  atpがlisting, write, print, printq, nlを受け取ったときはnil :: nilを返す. (write,print,printq,nlの例)    >[block, atp <- [write,68], atp <- nl] <- value. 68 nil :: nil >[block, atp <- [print,68], atp <- nl] <- value. number :: 68 nil :: nil >[block, atp <- [print,quote('I am a cat.')], atp <- nl] <- value. I am a cat. nil :: nil >[block, atp <- [printq,quote('I am a cat.')], atp <- nl] <- value. 'I am a cat.' nil :: nil >[block, atp <- [print,quote(renkon)], atp <- nl, atp <- [print,quote(hoorensoo)], atp <- [print,quote(kyuuri)], atp <- nl ] <- value. renkon hoorensookyuuri nil :: nil read  式を読み込んで,その式の値を返す. (例)    >atp <- read. 63.       (入力) number :: 63. 5.5.3 prolog  prologというオブジェクトにPrologのゴールをメッセージとして送ると,prologはそのゴールを実行する.prologは,そのゴールの実行が成功したならばtrue :: trueを返し,失敗したならばfalse :: falseを返す. (例)    >prolog <- 23 > 17. true :: true >prolog <- 17 > 23. false :: false  ゴール列を括弧で囲んだものをメッセージとしてprologに送ると,prologはそのゴール列を実行する. (例) >prolog <- (A is 4*7, write(A), nl). 28 true :: true 5.5.4 dos  dosというオブジェクトにMS-DOSのコマンドをメッセージとして送ると,dosはそのコマンドを実行する. (例) >dos <- 'dir b:'. ドライブ B: のディスクのボリュームラベルは TEXT ディレクトリは B:\ ATP BAK 12803 90-04-02 13:27 ATP DOC 13897 90-04-02 19:28 ATP PL 6480 90-04-02 8:56 4 個のファイルがあります. 613376 バイトが使用可能です. nil :: nil 5.6 file  fileというクラスのインスタンスは,何らかのファイルである.ファイルを参照するための定数は,    file :: ファイル名 と書く. (例)    file :: 'b:kirin.atp' file :: 'a:atp.pl'  fileのインスタンスが持っているメソッドは次の通りである. edit  エディタを起動して,レシーバを編集することができる状態にする.そして,エディタが終了したのち,レシーバをロードする.たとえば,'b:maguro.atp'というファイルを編集してロードしたいときは,    >file :: 'b:maguro.atp' <- edit. と,インタプリタに入力すればよい. load  レシーバをロードする.たとえば,'b:utsubo.atp'というファイルをロードしたいときは,    >file :: 'b:utsubo.atp' <- load. と,インタプリタに入力すればよい. 6 プログラム 6.1 ATPのプログラム  ATPでプログラムを書くというのは,なんらかのクラスに対して新しいメソッドを追加するということである.標準クラスにメソッドを追加することもできるし,新しいクラスを作ることもできる.さらに,スーパークラスというものを定義することによって,ひとつのクラスを特殊化したようなクラスを作ることもできる.  クラスにメソッドを追加したいときは,メソッドの定義というものを書けばよい.ATPのプログラムは,スーパークラスの定義とメソッドの定義を並べたものである.  ATPのプログラムの構文は,    スーパークラスの定義の列    メソッドの定義の列 である.スーパークラスの定義をメソッドの定義の下や途中に書くことはできない.  ATPのプログラムの中には,注釈を書くこともできる.先頭が/*で末尾が*/であるような文字列,および,先頭が%で末尾が改行であるような文字列は注釈であるとみなされる. 6.2 メソッドの定義 6.2.1 メソッドの定義の構文  メソッドの定義は,次のように書く.  method(クラス名,メッセージ・パターン,変数の宣言,ブロック).  この中に書くクラス名というのは,定義されるメソッドはどのクラスのものであるかということを指定するものである.  メッセージ・パターンは,定義されるメソッドはオブジェクトがどのようなメッセージを受け取ったときに実行されるものであるかということを指定するものである.  変数の宣言は,メソッドの定義の中で使われる局所的な変数を宣言するもので,    [変数名,変数名,・・・,変数名] と書く.局所的な変数をまったく使わない場合でも,    [] というものを書く必要がある.  最後のブロックは,メッセージを受け取ったオブジェクトが何を実行するかということを指定するものである.オブジェクトは,メッセージを受け取ると,そのメッセージに対応するメソッドの中のブロックにvalueを送り,そのブロックが返した値(つまりブロックの中の最後の式の値)を返す.  非常に簡単なメソッドの例として,takoというクラスのインスタンスがashiというメッセージを受け取ったときはつねにnumber :: 8というオブジェクトを返すというメソッドの定義を書いてみる.    method(tako,ashi,[],[block,8]).  このプログラムをロードすれば,次のように,takoのインスタンスにashiというメッセージを送ることによって,タコの足の数を知ることができる.    >tako :: haruhiko <- ashi. number :: 8  次の例は,usagiというクラスのインスタンスがclass_printというメッセージを受け取ったときはusagiという文字列と改行を出力するというメソッドの定義である.    method(usagi,class_print,[],[block, atp <- [print,quote(usagi)], atp <- nl ]).  usagi::hirokoにclass_printを送ると,次のようになる.    >usagi :: hiroko <- class_print. usagi nil :: nil  局所的な変数を使う例として,atpがseventeenというメッセージを受け取ったときは1, 2, 3, ..., 17を出力するというメソッドの定義を書いてみる.    method(atp,seventeen,[i],[block, i := 0, [block, i <- [<,17]] <- [while_true,[block, i := i <- [+,1], atp <- [write,i], atp <- [print,quote(' ')] ]], atp <- nl ]).  実行結果は次のようになる.    >atp <- seventeen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 nil :: nil 6.2.2 self  メソッドの定義の中では,selfという式によってレシーバを参照することができる.たとえば,iruka :: tatsuyaというオブジェクトがメソッドを実行しているとすると,その間だけ,selfという式の値はiruka :: tatsuyaである.  selfを使うメソッドの例として,numberのインスタンスがnibaiというメッセージを受け取ったときは,自分の2倍を返すというメソッドの定義を書いてみる.    method(number,nibai,[],[block, self <- [*,2] ]).  実行結果は次のようになる.    >70 <- nibai. number :: 140 6.2.3 引数  引数を含むメッセージを受け取って動作する,というメソッドの定義を書くためには,メッセージ・パターンの中に仮引数を書かなければならない.仮引数は,先頭の文字が英大文字であるような,英字または数字の列である.メッセージがオブジェクトに送られると,そのメッセージの中の実引数とメッセージ・パターンの中の仮引数とが対応づけられ,ブロックの中に書かれた仮引数は,実引数に置き変わる.  例として,numberのインスタンスが[max,]というメッセージを受け取ったときは,レシーバがよりも大きいならばレシーバを返し,そうでなければを返す,というメソッドの定義を書いてみる.    method(number,[max,A],[],[block, self <- [>,A] <- [if_true,[block,self], if_false,[block,A]] <- value ]).  実行結果は次のようになる.    >23 <- [max,27]. number :: 27 >23 <- [max,18]. number :: 23 6.3 スーパークラスの定義  ATPでは,クラスとクラスとの間に,上位・下位の関係を定義することができる.上位のクラスを,下位のクラスのスーパークラスといい,下位のクラスを,上位のクラスのサブクラスという.あるクラスのオブジェクトが,そのクラスの中では定義されていないメソッドのメッセージを受け取ったとき,もしもそのクラスのスーパークラスが定義されていたとすると,そのオブジェクトは,そのスーパークラスのメソッドを実行する.  スーパークラスの定義は,    superclass(スーパークラス,サブクラス). と書く.たとえば,    superclass(doobutsu,ningen). と書けば,doobutsuはningenのスーパークラスであるという定義になる.  スーパークラスの定義の列は,プログラムの先頭に書かなければならない.  スーパークラスの定義を含むプログラムの例を書いてみる.    superclass(ningen,nihonjin). method(ningen,ashi,[],[block,2]). method(nihonjin,gengo,[],[block,quote(nihongo)]).  このプログラムを実行してみる.    >ningen :: tadashi <- ashi. nunber :: 2 >nihonjin :: masako <- gengo. nihongo >nihonjin :: masako <- ashi. number :: 2  このように,nihonjinのインスタンスにashiというメッセージを送った場合,nihonjinというクラスにはそのメッセージに対応するメソッドが定義されていないので,レシーバは,nihonjinのスーパークラスであるningenが持っているメソッドを実行する.