クロージャを学ぶ その心は?
とある事を以前、職場の上司に言われた。
プログラマを何年もやっていてもわからないことは多い。
実は基本的なこともしらなかったこともあるしよくわかってなかった、と言うのは本当によくある。
大切なのはよくわからないまま放置しないことだ。
はい、わからないまま放置していること多いです!
というわけで、今回はよくわからない基本的なことの1つ、クロージャをお勉強。
クロージャって何?
Wiki先生に聞きましょう。
クロージャ(クロージャー、英語: closure)、関数閉包はプログラミング言語における関数オブジェクトの一種。
いくつかの言語ではラムダ式や無名関数で実現している。
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。関数とそれを評価する環境のペアであるともいえる。 主な利点としてはグローバル変数の削減が挙げられる。
クロージャ - Wikipedia
ちょっとよくわかりません。日本語でry
色々なサイトで勉強してみて、ようやく意味がつかめました。
下の2点がクロージャの特徴です。
引数以外の変数を実行時の環境ではなく、自身が定義された環境で(以下略)
これは、関数そのものをオブジェクトとして持って、引数以外の変数は前回実行時の状態を保存しておく、
ということです。
主な利点としてはグローバル変数の削減が挙げられる
上の前回その関数を実行した状態を保存しておく、という利点を使って、
その関数が実行された回数をわざわざグローバルで持たず関数オブジェクトで保持することが可能になります。
自分的には、クロージャとは
関数の状態を保存して、いつでもその状態で処理を始められる方法と解釈しました。
クロージャの実際の例
よくあるカウンタの例です。
#encoding : utf-8 def increase n = 0 -> { n += 5 } end 3.times { p increase.call } puts "===========" increase_proc = increase 3.times { p increase_proc.call }
この例についての解説は、@ITの記事を参照してください。
他のサイトも見たのですが、ここが詳細にかかれてわかりやすくなっています。
http://www.atmarkit.co.jp/ait/articles/1409/29/news035_3.html
自分がよくわからなかったところ
increase_procという関数オブジェクトを使うことで、nが5,10,15となっていくのはわかります。
でも、n=0の部分はなぜ無視されてしまうのか?これがよくわかりませんでした。
ちょっと悩んだ結果、以下のことに気づきました。
increaseメソッドの返り値は->{ n += 5 }、つまりProcクラス
つまり、increase_procはincreaseメソッドの関数オブジェクトと勘違いしてたわけです。
本当は上の通りProcオブジェクトの中身の関数オブジェクトだったということでした。
勘違いした原因
前職ではC++をやっていたのですが、そのC++に関数ポインタと言うものがありました。
この関数ポインタは、下のような感じで使います。
int Sum(int a, int b){ return a + b; } int (func*)(int, int); func = Sum;
※C++なら関数オブジェクトを使ったほうが良いです
func = Sumの下りが似てたんですね…
この場合、Sumという関数のポインタをfunc変数に保存する、と言う事になります。
なので、Sum関数全部が1つのfunc変数に入ってます。
C++に慣れてる人は気をつけてください!(こんな勘違いするのは自分だけ)
クロージャをどう使っていくか
上の特徴にある通り、グローバル変数を削減したいといったシチュエーションで使えると思います。
ただ、基本的に私はグローバル変数を使わない実装にしているので、使う機会は余りなさそうな。
これに関しては色々なサイトを見たり自分で使ったりして自分なりの使い方を見つけていく必要がありそうです。
参考文献・サイト
おまけ
このクロージャはメタプログラミングの技法の1つとされています。
RubyをRubyたらしめるのはこのメタプログラミング、ともいわれているのでメタプログラミングをしっかり勉強してみたいところです。
さしあたっては以下の本を読むところから始めないと。
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (2件) を見る