関数型プログラミング事始め (24) コマンドも関数 - Lisp超入門13(最終回)
関数型プログラミングがはじめての方へ贈る入門の書
前節:属性リストは万能 次節:再帰プログラミング(1)
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)
(16) コマンド
Lisp処理系では、コンパイラやデバッガ、エディタなどに対するコマンドも通常の関数として、定義しています。
Common Lispでは言語仕様レベルでコマンドも関数として定義されています。例えば、compile, compile-file, disassenble, trace, step, time, describe, inspect, room, edなどの関数が言語仕様として定義されています。 逆に言えば、言語仕様で開発環境のコマンドも定義されていますので、言語処理系に開発環境が含まれていて、その結果、Common Lispは世界最大の言語仕様となりました(言語仕様書のページ数が1000ページを超えています)。
一方、ISLispはCommon Lispの巨大さを反省して、ISOで規定された言語仕様です。このため、上記のコマンドは言語仕様には含まれておらず、言語処理系の実装依存になっています。
ここではOK! ISLispのコマンド関数を紹介していくことにします。基本的にCommon Lispのサブセットになっています。まずはislispを起動してください。なおISLisp処理系はLisp処理系の導入で紹介していますので参照してください。
> ISLisp Version 0.80 (1999/02/25)
>
ISLisp>
(a) OK! ISLispのコマンド関数一覧
OK! ISLispでは以下のコマンド関数を定義しています。
関数: LOAD
仕様: (LOAD FILE) ---> T
説明: ファイル file をロードする
関数: TIME
仕様: (TIME FORM) ---> <OBJECT>
説明: フォーム form を実行し経過時間を表示する(特殊形式)
関数: ROOM
仕様: (ROOM) ---> <NULL>
説明: 現在のメモリ使用状況を表示する
関数: EVAL
仕様: (EVAL FORM) ---> <OBJECT>
説明: フォーム form を評価する
関数: COMPILE
仕様: (COMPILE FUN) ---> BOOLEAN
説明: 関数 fun をコンパイルする
関数: COMPILE-FILE
仕様: (COMPILE-FILE FILE) ---> BOOLEAN
説明: ファイル file をコンパイルする
関数: COMPILE-FILES
仕様: (COMPILE-FILES DST-FNAME SRC-FNAME *) ---> BOOLEAN
説明: src-fname の複数のファイルを dst-fname のファイルにコンパイルする
関数: DESCIBE
仕様: (DESCIBE OBJ) ---> IDEF
説明: obj の内容を表示する
関数: GC
仕様: (GC) ---> <NULL>
説明: gc を強制的に実行する
関数: WRITE
仕様: (WRITE OBJECT STREAM +) ---> <NULL>
説明: object を stream に表示する
関数: QUIT
仕様: (QUIT) ---> TRANSFERS-CONTROL
説明: ISLisp処理系を終了する
(b) コマンド関数の実行
それではOK! ISLispでコマンド関数を実行してみます。
まず、testフォルダを作成し、そのフォルダにfact.lspのテキストファイルを以下のように作成しておきます。
(defun fact (n)
(if (< n 1)
1
(* n (fact (- n 1))) ))
(defun tarai (x y z)
(if (<= x y)
y
(tarai (tarai (- x 1) y z) (tarai (- y 1) z x) (tarai (- z 1) x y))))
そして、以下のように実行します。
ISLisp>(load "test\\test.lsp")
T
ISLisp>(fact 100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
ISLisp>(tarai 12 6 0)
12
関数 load はLispのプログラムファイルをロードします。(load "test\\test.lsp")はファイル"test\test.lsp"をロードします。なお、\(バックスラッシュ、YENマーク)はLispではエスケープキャラクタになっていますので、バックスラッシュを入力するには\\とします。
次に時間計測関数timeとコンパイル関数compileを紹介します。
ISLisp>(time (tarai 12 6 0))
Elapse time = 1.547 sec.
GC: 0 Stack Used: 1125
CONS: 0 (GC: 0)
SYMBOL: 0 (GC: 0)
HEADER: 0 (GC: 0)
VECTOR: 0 (GC: 0)
12
ISLisp>(compile 'tarai)
TARAI
ISLisp>(time (tarai 12 6 0))
Elapse time = 0.625 sec.
GC: 0 Stack Used: 519
CONS: 0 (GC: 0)
SYMBOL: 0 (GC: 0)
HEADER: 0 (GC: 0)
VECTOR: 0 (GC: 0)
12
(time (tarai 12 6 0))では、(tarai 12 6 0)の実行時間を計測します。
Elapse time = 1.547 sec.のように、経過時間(システム時間を含む)は1.547秒で、実行中にGC(ガベージコレクション)は起動せず、スタックの使用量は1125であったことを示されます。
(compile 'tarai)では関数taraiをコンパイルします。続いての(time (tarai 12 6 0))で経過時間が半減して高速化され、スタックの消費量も少なくなっていることがわかります。
なおOK! ISLispのコンパイラはバイトコードコンパイラで実装していて、Lispのソースプログラムをバイトコードに変換して、バイトコードインタプリタで実行するようにしています。バイトコードコンパイラなので、他のCPUへの移植性は高いのですが、ネイティブコードコンパイラに比べて実行時間は遅くなっています。
最後にオブジェクトの内容を表示する関数 describeを紹介します。これで関数の定義も見ることができます。
ISLisp>(describe #'fact)
External representaion: #<UFUNCTION 0025CC36: FACT>
Object code : 0025CC36
user defined function
argsize : 1
flags : 0004
body : ((IF (< N 1) 1 (* N (FACT (- N 1)))))
lambda list: (N)
#<IDEF>
(describe #'fact)で関数factの定義を見ることができます。なおコンパイルされた関数に対してはバイトコードのサイズだけが表示されます。
(c) (参考)エラーブレーク
なおOK! ISLispでは関数実行中にエラーになると、エラーブレークのread-eval-printループのモードになります。このときはスタック上にあるローカル変数にもアクセスでき、これを利用してのデバッグができます。
エラーブレークに入ったときに:hを打鍵することで、ここで使えるコマンドが以下のように表示されます。
Error:1>:h
ISLisp Debugger commands:
>>> :q quit ISLisp.
>>> :B print backtrace.
>>> :b print backtrace(user function).
>>> :s print stack.
>>> :m print debugger menu.
>>> :h print this message.
>>> otherwise evaluate.あああ
(d) その他の関数
Lispは前置形式の「(付録)ISLisp 関数一覧」で、上記のコマンド関数を含めたISLispの関数一覧を紹介していますので、参照してください。
またISLisp検証システムをISLisp Home Pageで公開していますので、こちらも参照してください。
今回でISLispの超入門は終わりになります。次回はこのISLispを使って、関数型プログラミングの入門を開始する予定です。お楽しみに。
参考:プログラミング言語はどれがお得?(前編)|五味弘 (note.com)
参考:プログラミング言語はどれがお得?(後編)|五味弘 (note.com)