見出し画像

関数型プログラミング事始め (24) コマンドも関数 - Lisp超入門13(最終回)

関数型プログラミングがはじめての方へ贈る入門の書
前節:属性リストは万能 次節:再帰プログラミング(1)
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)

Lisp処理系では、処理系に対するコンパイラやデバッガなどのコマンドも関数で定義します。Common Lispでは各種コマンドも言語仕様の一部として定義されています。一方、ISLispではコマンドは言語仕様としては定義されておらず、処理系依存になっています。
コマンドも普通の関数として定義されているため、プログラムの実行中にコマンドを呼び出すことが可能です。このため、プログラムの実行中にデバッグしてプログラムを修正し、そのまま実行を再開することも可能です。これを利用すると効率的なデバッグができますが、一方で混乱する状況になり、注意する必要があります。

(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)








いいなと思ったら応援しよう!

五味弘
よろしければサポートをお願いします!