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