Land of Lisp勉強ノート#1-2

Land of Lisp LISP
Land of Lisp

このカテゴリーのブログは僕の個人的なノートです。
決して初心者が参考にしてはいけません。毒があると大変ですので。

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

  1. 実際にはREPLにおける関数であるが、シェルと同じ理由でコマンドと呼んでおく。 ↩︎

コメント

タイトルとURLをコピーしました