このカテゴリーのブログは僕の個人的なノートです。
決して初心者が参考にしてはいけません。毒があると大変ですので。
1章 さあLispを初めてみよう
Lispはプログラミング言語だ。生まれは1953年あたりで、僕より若い。
Lispにも方言がある。何百というものがあるみたいだが、今では大きく2つだ。
- ANSI Common Lisp
- Schema
新しいLisp達もいるが、意識しなくて良しだ。
本ではANSI Common Lispを扱うみたいなので、僕もそれに倣おう。
スクリプティング分野でのLispもある。Emacs LispはEmacsテキストエディタで使われている。
Lispはパラダイム言語だ。様々なプログラミング手法をサポートする。がしかし、基本の関数手法だな。
Lispの勉強を開始するにあたって、自分のUbuntuにclispパッケージ(Common Lisp)をインストールした。今後Lispを使う際はREPL(read-eval-printループ)を使う。
本ではゲームを書くらしい。
2章 はじめてのLispプログラミング
2.1 数当てゲーム
REPLにおける対話ゲームを作る。
利用者は1~100の間の数字を頭に浮かべ、コンピュータは予測しながらその値に近づいてゆくのだ。手法はバイナリーサーチである。
下記の定義を行う。
(defparameter *small* 1)
(defparameter *big* 100)
(defun guess-my-number ()
(ash (+ *small* *big*) -1))
(defun smaller ()
(setf *big* (1- (guess-my-number)))
(guess-my-number))
(defun bigger ()
(setf *small* (1+ (guess-my-number)))
(guess-my-number))
(defun start-over ()
(defparameter *small* 1)
(defparameter *big* 100)
(guess-my-number))
ここでは下記のコマンド1を作る。サイトはグローバルだ。
- guess-my-number 今の範囲(*small*と*big*)の中間点を計算する
- smaller 答えが小さい場合
- bigger 答えが大きい場合
- start-over 開始時の初期化を行う
2.2 Lispでグローバル変数を定義する
smallとbigと言うグローバル変数(どこからでも見えるというあれだ)を定義している。
変数を*で囲むのを「耳あて」と言い、お作法である。
同じ定義を違う値で行うと上書きされる。
下記の場合*foo*は6に変わる。
(defparameter *foo* 5)
(defparameter *foo* 6)
ところがdefvarでは変化しない。
値は5のままである。
この本ではdefparameterを使うこととする。
(defvar *foo* 5)
(defvar *foo* 6)
2.3 基本的なLispのエチケット
コマンドやそのパラメーターは括弧()で括る。
(defparameter *nanika* 123)
改行、インデントは無視されるのでこの書き方は同じ結果を生む。
(defparameter
*nanika*
123
)
コマンドは実は関数である。従ってREPLに入力する際は括弧で括ること。
このゲームで提示された値が自分の想定よりも小さなときには、下記を入力する。
(bigger)
2.4 グローバル関数を定義する
基本構文はこうである。グローバルとはどこからでも見えるというあれだ。
(defun function-name (arguments)
...)
guess-my-number関数の定義
(defun guess-my-number ()
(ash (+ *small* *big*) -1))
*small*と*big*の合計を半分にしている。
例えばこのゲームの最初には、このゲームでは*small*と*big*は1と100なので
50が返ることになる。そしてREPL上に表示される。
smallerとbigger関数の定義
(defun smaller ()
(setf *big* (1- (guess-my-number)))
(guess-my-number))
(defun bigger ()
(setf *small* (1+ (guess-my-number)))
(guess-my-number))
smaller関数は「より下」と言う指示のコマンド。
bigger関数はその逆。
setf関数の”1+”や”1-“は「1を足す」や「1を引く」の意味である。
上と言われたら下限を中間値にして、下と言われたら上限を中間値にする。
(つまり、壁を動かしている)
その後にguess-my-number関数にて次の中間値を計算しているのだ。
どちらも最後には調整済みの中間値を(利用者に見えるように)出力している。
この辺からLispの片鱗が。
start-over関数の定義
(defun start-over ()
(defparameter *small* 1)
(defparameter *big* 100)
(guess-my-number))
ゲームを開始する際にお祓いするコマンドを作っておく。
グルーバル変数の*small*と*big*に初期値(1と100)を設定する。
その後に中間値を出力するのだ。つまりREPL上で見えるのだ。
2.5 ローカル変数を定義する
コードブロックや関数内だけで有効な変数を定義するにはlet関数を使う。
(let ((a 5)
(b 6))
(+ a b))
結果は11が返る。let関数のブロック内で変数:aと変数:bが定義されていて、
最後の行で一時に呼び出されている。
お作法通りならa,bはグローバルではないということになるが、そうでもないところに注意。
2.6 ローカル関数の定義
ロカル関数はflet関数を使う。
(flet ((f (n)
(+ n 10)
(g (n)
(- n 3)))
(g (f 5))))
flet関数内でまずf関数とg関数の定義がある。最後の行でそれを使っている。
パラメータは5で、f関数は10加え、g関数は3引くので、結果は12である。
同じスコープ内で定義された関数を使いたい時はlabels関数を使う。
(labels ((a (n)
(+ n 5))
(b (n)
(+(a n) 6)))
(b 10))
b関数の処理定義においてa関数が使われている。
labelsにすれば関数を参照しあってもよくなる。
また、自分自身を呼ぶ再帰の際にも使える。
2.7 本章で学んだこと
・グルーバル変数定義関数:defparameter
・グローバル関数定義関数:defun
・ローカル変数定義関数:let
・ローカル関数定義関数:flet
・再帰させるローカル関数定義関数:labels
- 実際にはREPLにおける関数であるが、シェルと同じ理由でコマンドと呼んでおく。 ↩︎
コメント