純粋なPHPerの人が初めてASP.NETのコードを見て???となるのがLinqとラムダ式ではないでしょうか。
ここでは、ウンチクはさておき、最低限理解しておきたいLinqとラムダ式の簡単なサンプルを書いておきます。
ここで紹介するのはLinqやLambda記述のごくごく1部です。あとはGoogle先生に聞いて下さい。
Linq(リンク)
SQLはDBに特化したクエリ言語ですが、リンクは、コードの中で扱う色々なオブジェクトに対してクエリを書けられる技術です。
なので、SelectやらWhereやらというキーワードが出ます。ただ、記述方式はSQLとは違うので、そこは慣れましょう。
Lambda(ラムダ)式
関数を簡潔に書く記述形式。
元々は、
- 関数の引数とか(戻り値)に「関数」を利用しないといけない時に、
- 引数の場所に関数をそのまま書くと長くなったり、
- わざわざ別途関数を定義するのがめんどくさい
場合などに、コードを簡潔く方法として登場しました。
上記のようなシチュエーションがイベントハンドラを記述する際に多く当てはまるということです。
最近では、Java8に導入されたり、関数型言語のブームでよく話題になります。
まあ、ここでは、なんとか記述の意味を理解出来る程度の知識をつけましょう。
準備
簡単なコンソールアプリケーションを作って動作を確認します。
ベースとなるコードは
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
//表示
foreach(int x in data)
{
Console.WriteLine(x);
}
//待つ
Console.ReadLine();
}
}
}
とします。
配列を定義して、ただ出力しているという簡単なものです。
このコードを動かすところまでは自力でお願いします。
実行結果は、
1
2
3
4
5
6
7
8
9
10
となるはずです。キーを押すと終わります。
とりあえず試してみる
では、上記のコードにLinqの要素を追加したサンプルを見てみます。
ある配列を2倍した配列を得るというものです。
SQLでいうと、
select x * 2 from data;
という雰囲気でしょうか。
このコードでは、data.Select()という記述がLinqです(他にもいろいろな書き方があります)。
ここでのLinqのSelect関数は引数として下段で定義されているbai関数を引数として受け取っています。こう記述することで、data配列の中身が2倍され、results配列に格納されます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
var results = data.Select(bai);
//表示
foreach(int x in results)
{
Console.WriteLine(x);
}
//待つ
Console.ReadLine();
}
//2倍する関数
static int bai(int x)
{
return x*2;
}
}
}
実行結果は、
2
4
6
8
10
12
14
16
18
20
となります。2倍されています。
なんだかよくわからないかもしれませんが、要点は、
- Linqではdata.Select()という感じの記述でデータ取得・操作ができる。
- Linqでは、操作の条件(引数)を関数として渡す(場合がある)。
ということだけ理解してくれれば十分です。
Lambda(ラムダ)式を理解する
では、サンプルをベースにラムダ式を見て行きましょう。
やりたいことは、Linqの引数として渡す関数を簡単に記述しましょうということです。
匿名関数
いきなりラムダ式で書いてもいいのですが、歴史を遡るため、まずは匿名関数の利用からみてみましょう。
なお、歴史についてはこちらがよくまとまっています。
匿名関数とはいちいち関数を名前をつけて定義しなくても、(名前を付けず、匿名で)引数の中に直接記述できるようにしたものです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
var results = data.Select(delegate(int x){return x * 2;});
//表示
foreach(int x in results)
{
Console.WriteLine(x);
}
//待つ
Console.ReadLine();
}
}
}
実行結果は、先のサンプルと同じなので割愛します。
ポイントは、
var results = data.Select(delegate(int x){return x * 2;});
の部分です。外部定義関数(bai)はなくなり、data.Select()の中に直接関数が記述されています。
delegateやら匿名関数やら言われると難しそうですが、お馴染みjQueryとかでよく見る形式です。
$("#hoge").click(function(){
//
});
こういうやつ。
あと、Laravelのルート定義とか、
Route::get('/',function(){
return "hoge";
});
みたいな。
Lambda
いよいよ本題のラムダ。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
var results = data.Select(x => x * 2);
//表示
foreach(int x in results)
{
Console.WriteLine(x);
}
//待つ
Console.ReadLine();
}
}
}
実行結果は、今までと同じです。
もちろんポイントは、
var results = data.Select(x => x * 2);
の部分です。だいぶ短くなりました。
普通の関数、匿名関数のソースとコードを比較して、要素の対応関係に慣れてください。
引数、戻り値の型定義とか、不要なものが一切排除されています。
C#で=>がでてきたらラムダです(これはGo toと発音する)。もちろんPHPの配列定義とは関係ありません。
Linq
では、ラムダに慣れた?ところでLinqです。
上記のサンプルでは、Linqのメリットはほとんどありませんでした。が、次のサンプルだとどうでしょう。
Where
配列の中から5以上の値を抽出してみます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
var results = data.Where(x => x >= 5);
//表示
foreach(int x in results)
{
Console.WriteLine(x);
}
//待つ
Console.ReadLine();
}
}
}
ポイントはもちろん、
var results = data.Where(x => x >= 5);
です。Linqの関数はWhereになり、条件(関数)をラムダ式で記述しています。
データの抽出条件は都度変わるので、そのケースに応じた関数をいちいち用意したり、長々と引数に書いたりするのはナンセンスです。ラムダだとスッキリ記述できます。
First
Linqには他にもいろいろな便利な関数がありますが、ここでは訳あってFirstのサンプルを記述しておきます。
配列を走査し、はじめて5と一致する要素を出力するというものです。まあ、サンプルとしてはほとんど意味はありませんが、記述例です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cstest
{
public class Program
{
public void Main(string[] args)
{
//データ
var data = new[]{1,2,3,4,5,6,7,8,9,10};
var result = data.First(x => x == 5);
//表示
Console.WriteLine(result);
//待つ
Console.ReadLine();
}
}
}
戻りは配列ではなく、単数になるので、foreachでループはしていません。