今仕事で関わっている某アプリ開発の話。次のように書かれているコードの、まぁ、多い事、多い事。言語はC#ね。
class Foo
{
// 略
void Bar()
{
var someObj = new SomeObject();
// 例1
Action<int> lambda = x =>
{
// 略。なんかすげー長いラムダ関数
someObj.SetHoge(); // 引数のオブジェクトの破壊的メソッド呼び出してたりする。
// この先も長い
}
}
}
いや、うん、分かるよ? そう書きたくなる気持ちは。だが死ね。
要は、メソッド内でだけ有効なメソッドが欲しいって話だよね。意味ある処理の纏まり毎に別のメソッドにしたいけれど、そうやって切り出したメソッドが或る特定のメソッド内でしか呼ばれない時とか、しかも1度だけしか呼ばれない時とか、そう書きたくなるよね。でも死ね。
C#のラムダ関数/無名関数(以下、まとめて「無名関数」と呼ぶ)は、そのすぐ外のスコープのオブジェクトをキャプチャするようになっている(恐ろしい事に、他の大部分の言語でも、そういう仕様になっているようだ)。別のメソッドに分けられているように見えるが、完全なスコープの分離がなされていない。なので、ローカル変数が、それと同じスコープの無名関数内で書き換えられている可能性が捨てきれない。事実上、複数のメソッドに分けられる筈のクソ長いメソッドを作ったのとそう変わらない。
C++のラムダ関数みたいに、キャプチャする変数を明示的に指定する仕様ならまだマシなんだろうけどね。
void foo()
{
auto i = 1;
// C++のラムダ関数。デフォルトでは何もキャプチャしない
auto lambda1 = []() {
i = 10; // エラー
};
// iをキャプチャ。ただし値のコピーなので、foo()のiには影響しない。
auto lambda2 = [i]() {
i = 10; // OK
};
// iをキャプチャ。参照のキャプチャなので、foo()のiを書き換える。
auto lambda3 = [&i]() {
i = 10; // OK
};
}
変数のキャプチャというよりも、無名関数が糞長い事が根本的な原因かもしれないけれど。無条件の変数のキャプチャと、糞長い無名関数が合わさると、ホント凶器になるので、そういうコード書いてるプログラマは今すぐ死んで欲しい。死ぬのがダメだというならば、もういっそ禁止令出したい。俺が死ぬ前に。
ラベル:lambda_function