僕は、Javaでプログラミングを始めて8年以上になります。
ここ数年、RubyやScalaを少しずつ学んでいます。
やっと、さっとWebアプリケーションを作るのに、
Ruby(Sinatra)で作れるようになってきました。
Ruby、Scalaと進んでいくと、
Javaになかった概念がいろいろ出てきて、
ちょっとあやふやなまま進んでいるところもあります。
そういった概念のうちいくつかを、整理してみます。
クロージャ
JavaScriptにもあって、何となくわかって/使っているけど、
説明しろと言われると少し困ってしまう。
それがクロージャ。
クロージャとは、こう定義されます。
「レキシカルスコープを伴うファーストクラスの関数」
言語設計者たちが考えること p.176
言語設計者たちが考えること (Theory in practice)
- 作者: Federico Biancuzzi,Shane Warden,伊藤真浩,頃末和義,佐藤嘉一,鈴木幸敏,村上雅章
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/09/27
- メディア: 大型本
- 購入: 9人 クリック: 368回
- この商品を含むブログ (47件) を見る
なるほどなるほど。
レキシカルスコープを伴うファーストクラスの関数ね…
ってレキシカルスコープ?歴史狩るスコープ?となるわけです。
でも、この間、会社であんがいファーストクラスオブジェクトを知らないって
感じだったので、まずはそこから。
ファーストクラスオブジェクト
そのプログラミング言語で、何をファーストクラスオブジェクトとして扱えるのか、
というのは、けっこう大きなポイントです。
それがプログラマの思考に影響するからです。
ファーストクラスオブジェクトの定義は、次のようになります。
First-class object
第1級オブジェクトなどとも訳されるが、単にファーストクラスオブジェクトとも言われる。
Programming Language Concepts
定義としては、数学的オブジェクトとしての権利を全て取得しているオブジェクトのことである。詳細には、以下の能力を持つプログラム言語要素のことである。
1.オブジェクトに名前がつけられる。
2.引数として関数・手続に渡すことができる。
3.結果値として関数から帰って来ることができる。
4.データ構造に組み込むことができる。
通常は、単純なデータ又はデータ構造しかこのような身分を持つことはないのだが、関数型言語では、関数にもこのような身分を持たせている。
プログラミング言語の主となる構成要素であるものが、ファーストクラスオブジェクトであると。
これは、オブジェクト指向のオブジェクトとは、まったく関係ありません。
ではレキシカルスコープ。
レキシカルスコープ
静的スコープ(static scope)とも呼ぶそうです。
対義語が動的スコープ(dynamic scope)。
レキシカルスコープのみの説明だと、こういうことです。
引用:Wikipedia
http://ja.wikipedia.org/wiki/%E9%9D%99%E7%9A%84%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97
A { var x; } B { var x; // A内のxとは別物 C { var y; // Cの内側からしか見えない } }
変数のスコープが、構文から判断できるため、レキシカル(lexical)というわけです。
さらに、クロージャを考えるにあたっては、レキシカル環境というものがあります。
The commonly held minimalist definition of the lexical environment defines it as a set of all bindings of variables in the scope, and that is also what closures in any language have to capture.
http://en.wikipedia.org/wiki/Closure_(computer_science)#Lexical_environment
意訳。
一般的に考えられている、レキシカル環境の、ミニマムな定義としては、スコープ内の変数の一連のバインディングであるということです。それはまた、あらゆる言語においてクロージャが獲得しなければならないものでもあります。
つまり、クロージャはレキシカル環境を持っていないといけないと。
前述のレキシカル環境のWikipediaにある例(ちょっと変えてる)は、次のようなものです。
var f, g; var x = 0; function foo() { var x = 0; f = function() { return ++x; }; g = function() { return --x; }; x = 1; alert('inside foo, call to f(): ' + f()); // "2" } foo(); x++; alert('call to g(): ' + g()); // "1" alert('call to f(): ' + f()); // "2"
関数foo内にある変数xは、関数fooにバインディングされているので、グローバル変数のxとは独立して存在しています。
だから、下から3行目でx++;としても、foo内のxとは無関係。
クロージャは、関数オブジェクトと、その関数の中にあるローカル変数の状態をまとめている、というところでしょうね。
次はラムダ式についてまとめてみます。
ラムダ式
lambda formとかlambda expressionとか。
関数型言語に近づいていくほど、この言葉をよく聞きます。
C#にも搭載されてますね。
delegateが楽に書けるくらいの認識しかありませんでした。
で、MSDNには、こうあります。
ラムダ式とは、名前のない関数です。
関数 (F#)
とか、別のところではこう書いてあったり。
ラムダ式は式とステートメントを含めることができる匿名関数
ラムダ式 (C# プログラミング ガイド)
しかし、Python公式サイトにはこうあります。
Lambda forms (lambda expressions) have the same syntactic position as expressions.
5. Expressions — Python v3.2.3 documentation
They are a shorthand to create anonymous functions;
ラムダ式は、匿名関数を作成するためのショートハンドだ、とあります。
こうして、ラムダ式は記述方式なのか、匿名関数のことなのか、わからなくなってきます。
ラムダ関数
ところで、ラムダ関数という用語もあります。
ラムダ関数 (「匿名関数」と呼ばれることもよくあります) は単なる使い捨ての関数
PHP V5.3 では何が新しいのか: 第 2 回 クロージャーとラムダ関数
と、考えると、ラムダ式自体は記述方式であり、
ラムダ式で記述された関数は、匿名関数になる、と。
ラムダ関数は、匿名関数(無名関数)とイコールであるということでしょうね。