Lisp勉強中(2) 変数と関数とシンボル

まずはこの辺を覚えなきゃね。

変数

(set 'a 1)
(setq a 1)
(setf a 1)

これはみんな同じ意味っぽい。もっと別な使い方をするときは違いが出てくるらしいけど。

aっていう変数に1を代入している。だからaって打つと1が返る。

a
=>1

関数

関数を定義するのはdefun。

(defun a (price)
  (* price 1.05))
=>a

(a 100)
=>105.0

渡された値に1.05をかけて、税込み価格を計算する関数aを作った。そしてその関数に100を渡してみる。ちゃんと105が返ってきているのがわかる。

シンボル

変数と関数に同じ名前を付けることができる。同じ名前を付けても、別なものとして扱われる。変数名と関数名は名前空間が違うということ。

(setq a 100)
=>100

(defun a (price)
  (* price 1.05))
=>a

(a a)
=>105.0

だからこんな感じになる。aっていう変数に100を入れて、aっていう関数を作って、aっていう関数にaっていう変数を渡している。

で、この変数や関数の名前(入れ物)として使うaのことをシンボルって言う。

関数を変数に入れる

変数にも関数にも同じ名前をつけられるわけだけど、変数に関数を入れることもできる。さらにややこしくなる。ちゃんと区別して理解することが必要だと感じた。ダマシダマシ覚えてるとこの辺でついて行けなくなる。

(defun tax (price)
  (* price 1.05))
=>tax

まず上記のコードで税込み価格を求める関数taxを作った。

(setq b (function tax))
=>#<lexical-closure: tax>

そして、変数bに関数taxを入れた。以下は間違いなので注意。

(setq b tax)
=>エラー「変数が定義されていません: tax」

普通にtaxと書くと、変数としてのtaxだと思われてしまう。関数としてのtaxだよ、ということを伝えるために、(function tax)と書く。そしてこれはもっと短く書ける。

(setq b #'tax)
=>#<lexical-closure: tax>

#'はfunctionと同じ意味なんだそうだ。

さて、変数bに入った関数を実行してみる。

(b 100)
=>エラー「関数が定義されていません: b」

おっとこれは間違い。普通にbと書くと、関数としてのbだと思われてしまう。リストの最初の要素に書くのは関数名だから。しかしbは変数。中に関数が入っているけど、b自体は変数。変数の中に入った関数を実行するときはfuncallを使って以下のように書く。

(funcall b 100)
=>105.0

こんな感じ。

ここまでをまとめると、

(defun tax (price)
  (* price 1.05))
=>tax

(setq b #'tax)
=>#<lexical-closure: tax>

(funcall b 100)
=>105.0

まずtaxっていう関数を作って、それ自体(関数の処理結果ではなく、関数そのもの)をbという変数に入れた。そしてそれを実行した。

JavaScriptで書くとこんな感じ。

function tax(price){
    return price * 1.05;
}

var b = tax;

alert(b(100));

JavaScriptの場合は関数と変数の区別がホント無い。だから var b = tax; とか、b(100) とかが書けちゃう。#' とか funcall とかの仕組みが不要。その代わり、変数名と関数名に同じ名前を付けることは無理。

lambda

あらかじめdefunで関数を作らなくても、無名関数を作ってそのまま変数に入れてしまうこともできる。lambdaというのを使う。

(setq c (lambda (price)
          (* price 1.05)))
=>#<lexical-closure: (anonymous)>

(funcall c 100)
=>105.0

これをJavaScriptで書くとこんな感じかな。

var c = function(price){
    return price * 1.05;
};

alert(c(100));

こういうことであってる?

関数自体が変数に入ったことで、他の関数に引数として渡したり、何かの関数の戻り値として返したりすることができるようになる。つまり高階関数。ただ、単なる関数ポインタや無名関数とは違って、クロージャになってる。クロージャってのは、関数そのものと、それを実行する環境(変数などの存在状況)までがセットになってるもののこと。JavaScriptPerlRubyではおなじみのやつ。今後はJavaとかもクロージャを導入するらしい。知らなかったなら覚えておいた方が良い。便利だから。

Lispクロージャの例はもっとLispを覚えてから書く。今はまだLisp初心者すぎてクロージャの例が書けないのでね。