x はどこから来た?


はじめに

これは自分がラムダ式に出会った頃に受けた印象を元に書いた戯言的な記事です。
ちょっと雑な文章ですがご勘弁を、、、

x はどこから来た?

例えば、こんなコードを見かけたことがあるだろう。

C#
var hoge = foods.Where(x => x.Price >= 100).FirstOrDefault();

このコードがやっていることは、使われているトークンや式を見て何となく想像がつく。

x.Price >= 100

は、価格が100円以上だろうとわかる。
その式がWhereのカッコで囲まれていることから、SQL文を書いたことがあれば絞り込みを行うんだろうなと想像がつく。価格が100円以上の食べ物foodsのデータ絞り込みを行い、

.FirstOrDefault();

FirstOrDefaultの言葉どうりに先頭データを得ていると想像できる。
ただひとつ腑に落ちないことがある。それがxである。x.Priceとある。メンバを抱えているようだ。

xにドットを打つとIntelliSenseでリストが現れる。その中にPriceが見える。
このコードの前後の文は後述するが、xは後にも先にもこのカッコ内にしか現れない。

xはどこから来たのか?

突然、挨拶もなく現れた。=>もよくわからない。大なりイコール>=ではないな。見た目は右向き矢印だ。何となく左側のx=>で右側に送り込まれて仕事をしている風に見える。

xよ、お前は何者だ?

ラムダ式

この文をVB.NETで書き換えたらどうなるか。以下のとおりである。

VB.NET
Dim hoge = foods.Where(Function(x) x.Price >= 100).FirstOrDefault

C#より何をやってるかが分かりやすい。

Function(x)

は誰が見てもxを引数にとった関数である。その右隣はC#と一緒の式だ。

2つの言語により書き方は違えど、Whereのカッコ内はいずれも「ラムダ式」である。
ラムダ式とは、無名関数のことで、C#では=> の左辺が引数、右辺が実装(多くは単一の式)となっている。
VB.NETのラムダ式は、Function(x)とあるので誰が見たって関数だとわかる。通常のVB.NETの関数定義の場合、

Function 関数名(x)

となるが、無名なので関数名が省略されている。C#に至ってはFunctionすら無い。引数xがあるのみである。

Whereメソッド

ドットを打つとIntelliSenseのリストが現れるところから、何か実態が存在するかのように勘違いをしてしまうが、xは単なる関数の引数である。
xは何でもいい。yでも、zでも、argでも、foodfでもよい。関数を自作するとき、引数名を自分好みに勝手に宣言するのは当たり前のことだ。

C#
    class food
    {
        public string Name { get; set; }
        public int Price { get; set; }
        public food(string name, int price)
        {
            Name = name; 
            Price = price;
        }
    }
C#
        var foods = new List<food> { };
        foods.Add(new food("うどん", 80));
        foods.Add(new food("もやし", 30));
        foods.Add(new food("さんま", 120));
        foods.Add(new food("たまご", 200));
        var hoge = foods.Where(x => x.Price >= 100).FirstOrDefault();

Whereは、foodsのデータ型であるList<T>クラスの拡張メソッドである。拡張メソッドとは、雑に説明するとクラスそのものの実装を変えることなく後付けされたメソッドのことである。(ここでは詳しい説明を省略する)

Whereの引数は、シグネチャ(宣言構文)の定められたラムダ式である。ラムダ式なら何でも受け入れるということでは当然ない。
ラムダ式の左辺、xの型は定められている。List<food>の一つ一つ、foodクラスである。「うどん」であり「もやし」であり「さんま」であり「たまご」である。
IntelliSenseのリストにPriceが現れたのは型が定められているからだ。
右辺は引数xに関するユーザーが評価したいbool型を返す式である。foodsの全てのアイテムについて引数xを使って評価が行われる。想像するにWhereの中でfoodsがループで回され、そのループの中で右辺の式が使われているのだろう。
リスト内の全アイテムに対して自分が求める条件にあったアイテムだけを抽出することができる。様々な条件を指定できる。クラスをロジックで抽出できる。何という賢い仕組みだろう!

で、上の例でxから得たhogeの中身は「さんま」である。
「さんま」は120円か。今年(2020年)はそんなに安く買えないな、、、