よくやるプログラミングミス

LISP は自由?LISP は不親切?

LISP はシンプルな言語で、そのためプログラムの書き方も実に多様です。このことから、システム側でプログラムのエラーを警告してくれる機能は、文法や変数の型宣言で固めてある他のプログラミング言語にくらべると、ずいぶん不親切に思えるかもしれません。

コンパイルするタイプの言語では、コンパイルの過程で文法上のミスや変数の不正使用をかなり発見できますが、LISP では変数であるシンボルは特別な宣言をしなくても使えますし、あらゆるデータタイプを格納できるといった心強いもので、その負の面としてシステム側はその使用の正しさには関知しません。また、LISP ではアルゴリズム部分もプログラム内で生成する事ができるので、そのあたりをシステム側にチェックしてもらおうと思っても、実際無理な相談です。

これが LISP 言語のコンセプトと割り切って行く必要があります。

よくやるプログラミングミス(個人的体験)

LISP プログラミングをはじめて、犯しやすいプログラミングミスを、個人的な体験から挙げておきます。プログラムがうまく動かないと考える際の参考にしてください。

  1. 変数スコープの混乱
    ローカル変数を参照しているつもりが、ローカル変数を宣言し忘れて、外側の変数を書き換えてしまったり、ローカル変数の宣言が必要な外部のシンボルを隠してしまったりといったことを犯してしまいます。グローバルに宣言してある関数を呼び出そうと思っているのに、関数と同じ名前のローカル変数を作ってしまった場合は、LISP では関数でないものを関数として呼び出そうとして、実行時エラーが発生します。

  2. if 関数で progn を使い忘れている
    if 関数では、真のときの式や偽のときの式は一つしか指定できません。そのため progn 関数を使うのですが、ときには忘れてしまいます。最初は式が一つだったからよかったけど、改変を加えているうちにもう一つ式が加わったときなどです。これは、条件判断は正しく行っているようだが、結果で実行される部分がおかしいという状況であたってみると、よくやってしまっています。

  3. カッコの内側と外側の混乱
    LISP のプログラムはカッコだらけです。カッコが正しくないと動きません。でも、書いてみればなんとかなるものです。開きカッコと閉じカッコの数が合っているかは LISP システム側でもチェックしてくれます。それでも while 関数などのカッコの中には、多くの式が並ぶことがあり、カッコの対応が離れているためインデントしてあってもその対応がなかなか認識しづらいものです。そんな時に、カッコの内側に式を書いているつもりが、実は外側に書いてしまったなど、カッコの中で迷子になっていることがあります。

    while 関数を使わずに、高階関数を使って書き直してみると、カッコの対応も取りやすいシンプルなプログラムになることがあります。

  4. setq のし忘れ
    他のオプジェクト指向言語になれた頭だと、関数呼び出しの結果を setq で変数に保存するという当たり前のことを忘れることがあります。単純な例をあげますと (1+ n) は、n 変数の値に 1 を加えるという意味ですが、n オブジェクトに 1+ というメソッドを呼び出した気になって結果を変数に setq で保存することを忘れてしまうといったことです。個人的に実際には、append 関数を使う時に、このミスを犯しやすいです。

    関数は、原則、引数の元を改変しません。引数で渡されたリストを加工した結果のリストを関数から得る場合でも、引数の元のリストは改変されず、コピーによって関数内で別に確保されたリストが返ります。

  5. リストを quote し忘れて関数として実行!
    そのような関数は定義されていないという旨の実行時エラーのメッセージが出ますので、割とすぐ気が付きますが、分かっていても忘れたころにやってしまいます。