謎の機能クロージャその2

クロージャ - hokorobiの日記
http://d.hatena.ne.jp/hokorobi/20080513/1210693211
クロージャを使ったコマンド - 自称リバースエンジニアの詰め所
http://d.hatena.ne.jp/knenet/20080511/1210500286

 前に書いてたクロージャのやつ、何かやっぱり自分だけじゃなくて皆クロージャには色々思うところがあるというか、注目を引く機能らしいので、自分が思ってる所で適当にまとめをメモ。クロージャが分からない人が同じ道を辿れるかもしれない。理解した順番で書いた方がクロージャ*1を知りたい人には、いいのかもしれないけど、自分的に忘れないようなメモ。

Wikipediaによるクロージャの定義

クロージャ (Closure) は、プログラミング言語において引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する関数のことである。関数とそれを評価する環境のペアであるともいえる。

 なんというか、・・日本語が難しい。ので、適当にソースでメモ。というか、後で書いているコードとかが、本当にこのクロージャの定義に在っているのかすら自分には不明。だけど、多分便利には使えると思ったので、そういうクロージャらしき便利な何かの話。

■ほぼ等価なAとB

;A
(defun A ()
  (let ((var 2))
	var))

;B
(let ((var 2))
  (defun B ()
	var))
(A)
=>2
(B)
=>2

このままではAとBはほぼ等価だけど、varに処理を加えるとAとBで差が出る。

局所変数を保持する*2

;A
(defun A ()
  (let ((var t))
	(if var (setq var nil)
	  (setq var t))))

(A)
=>nil
(A)
=>nil

;B
(let ((var t))
  (defun B ()
	(if var (setq var nil)
	  (setq var t))))

(B)
=>nil
(B)
=>t

なんとなくletが前に出てると、関数呼び出した時に定義しなおさないので、初めのvarがtにリセットされないような感じ。

■何となく思ってる利点
 グローバル変数のように他のシーンでは使わないけど、同じ関数内では値を保持したい時とかに便利。保持するためだけにグローバル変数にしてしまうと、何処からでもアクセスできる変数名がもったいない感じ。変数へアクセスする関数が限定されてるなら、局所で値を保持しる。

■利用方法
 ただし、このままだ(B)をフラグとして利用しても、変数名を消費しない代わりに関数名を消費してて、あんまりメリットがない。ので、関数に機能を持たせて変数を同時に持てば、関数と変数分けるよりは名前の消費が少なくなって良い感じに。

ex1:なにか単純な機能を追加

;A
(defun A ()
  (let ((var 2))
	(setq var (1+ var))))

(A)
=>3
(A)
=>3

;B
(let ((var 2))
  (defun B ()
	(setq var (1+ var))))

(B)
=>3
(B)
=>4

ex2:同じ関数に違う名前

(let ((var 2))
  (defun B ()
	(setq var (1+ var))))

(setf (symbol-function 'B2) #'B)

(B)
=>3
(B2)
=>4
(B)
=>5
;B,B2は同じ値を持つ。

ex3:同じ機能の関数を量産

(defun foo()
  (let ((x nil))
	#'(lambda()
	   (if x (setf x nil)
		 (setf x t)))))

(setf (symbol-function 'hoge1) (foo))
(setf (symbol-function 'hoge2) (foo))

(hoge1)
=>t
(hoge2)
=>t
;hoge1,hoge2それぞれ別の値を持つ。

こうやって書くと他の言語で言う所のクラスとインスタンス見たいな感じに。こういう書き方をすると、一手間かかるというかfuncallが一々書くのが面倒という問題もあるけど。

■まとめ
自分も実用的な例はあんまり使った事ないというか、使い所が難しい気がする。と言う感じで書いたら。

クロージャを使ったコマンド - 自称リバースエンジニアの詰め所
http://d.hatena.ne.jp/knenet/20080511/1210500286

実用例?っぽいのを作ってくれてるので、こういう感じで人のソースを見るといいかもしれない。

*1:ここに書いている書き方や使いかたが本当にクロージャなのかは自分には分からないけど

*2:前は試した時にはletの変数を外から使うとか書いてたけど、どうもそういう理解よりも、ローカル変数の値を保持すると考えた方が、良いような気がした