C#でカリー化

LINQハァハァ→関数型言語?→Haskell!という感じに辿ってHaskellを勉強中なので、カリー化について。Haskell、の前にまずはC#で考える。例えば、たまーに見かける「a => b => c => a + b + c」のようなものがパッと見意味分からない。Haskellでも型定義Int->Int->Intみたいことやるので、それと同じなわけっぽいですけれど。

ゆっくり分解すると、「=>」の左が引数、右が戻り値なのでa =>( b => c => a + b + c) つまり Func<int,???>。???の部分もカッコでくくって b =>( c => a + b + c) つまりFunc<int,Func<int,???>>。c=>a+b+cは、見たまんまなのでFunc<int,Func<int,Func<int,int>>>ということになる。

冷静に解きほぐせばそんな難しくない。というわけで準備運動が済んだので、カリー化関数を作ろう。

// カリー化する関数
static Func<T, Func<U, V>> Currying<T, U, V>(Func<T, U, V> func)
{
    return t => u => func(t, u);
}

// 非カリー化する関数
static Func<T, U, V> UnCurrying<T, U, V>(Func<T, Func<U, V>> func)
{
    return (t, u) => func(t)(u);
}

// 例として使うx+yを返す関数
static int Sum(int x, int y)
{
    return x + y;
}

static void Main()
{
    int num1 = Sum(3, 5); // 普通に、8

    // Sumをカリー化する、関数はデリゲートに包む必要がある
    var CurriedSum = Currying((Func<int, int, int>)Sum);
    int num2 = CurriedSum(3)(5); // 当然、8

    var BindedSum = CurriedSum(3); // 3を部分適用する
    int num3 = BindedSum(5); // 勿論、8

    var UnCurriedSum = UnCurrying(CurriedSum); // 非カリー化
    int num4 = UnCurriedSum(3, 5); // 当然、Sum関数と一緒
}

多分、あってる、と思いたい。カリー化とは大雑把に言って「f(x,y) = g(x)(y)」ということのようなので、そうなるようバラしたり戻したり。そういえばで気付いたのですが、関数/ラムダ式をデリゲートに包む方法は

Func<int> test1 = () => 1;
var test2 = (Func<int>)(() => 2);
var test3 = new Func<int>(() => 3);

意外とバリエーションがある、気がする。ようは省略出来るだけでしょって話っぽいけど。単独で定義するときは1番を用いるかなー。癖でとりあえずvar、って書いてから、あー、これはnewするのダルいケースだった、varじゃなくてちゃんと書かなきゃ、と後ろに戻ったりするのがよくあって笑えない。

クロージャ

関数を返す関数繋がりでついでに、関数型言語って何がすごいんですか - Gemmaの日記からロケットのコードのC#版を。

 static void Main()
{
    Func<int, Action> Rocket = n => () => Console.WriteLine((n > 0) ? (n--).ToString() : "liftoff");
    var F = Rocket(3);
    var G = Rocket(3);
    F(); F(); F(); F(); // 行数使うのもアレなので横に並べます
    G(); G();
}

中々なスッキリ具合。function retrunが必要ない、というのがC#ラムダ式の強みですねえ。JavaScriptは(IEのせいもあって)function{return}が必須なのがカッタルイ。そんなことを思うと、C#はとても身軽で、個人的にはある意味とてもLightweightだと感じてしまったりする。

インテリセンスがりがり、コードスニペットがりがり、コードフォーマッタがりがりな上に乗っかっているので、軽快というか、Lightweight Languageが短距離走選手のようだとしたら、VS2008+C#は重武装+ロケットブースターで、それ自体は鈍重であっても、結果的にブースター付きのは強烈なスピードだよね、みたいな。軽快に書き進められるの自分の力じゃなくてVisualStudioの力でしょ、と思わないときもないけれど。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive