LoginSignup
3
2

More than 5 years have passed since last update.

Func<T1~T16,TResult>に対する拡張メソッド(部分適用・カリー化・遅延評価)

Posted at

Func に対する拡張メソッド

汎用デリゲート Func に対する拡張メソッドを作成しました。

Func<TResult>
Func<T, TResult>
Func<T1, T2, TResult>
Func<T1, T2, T3, TResult>
(省略)
Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>

.NET 4.0 では最大16個の引数を持つデリゲートが定義されているわけですが、全ての Func に対応したソースコードがネット上に見つからなかったので作成しました。GutHub にアップしてあります。(かなりニッチな内容で、需要はあまりないかもしれませんが)
【GitHub】mxProject/Misc

カリー化

Func<T1, T2, T3, TResult>Func<T1, Func<T2, Func<T3, TResult>>>に変換します。

カリー化
/// <summary>
/// Converts the specified delegate to a curried delegate.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func)
{
    return arg1 => arg2 => arg3 => func(arg1, arg2, arg3);
}

// 例
internal static DateTime GetDate(int year, short month, byte day)
{
    return new DateTime(year, month, day);
}

Func<int, short, byte, DateTime> f = GetDate;
Func<int, Func<short, Func<byte, DateTime>>> curry = f.Curry();

var result1 = GetDate(2018, 2, 28);
var result2 = curry(2018)(2)(28);

カリー化の解除

Func<T1, Func<T2, Func<T3, TResult>>>Func<T1, T2, T3, TResult>に変換します。

カリー化の解除
/// <summary>
/// Converts the specified curried delegate to a uncurried delegate.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public static Func<T1, T2, T3, TResult> Uncurry<T1, T2, T3, TResult>(this Func<T1, Func<T2, Func<T3, TResult>>> func)
{
    return (arg1, arg2, arg3) => func(arg1)(arg2)(arg3);
}

// 例
internal static DateTime GetDate(int year, short month, byte day)
{
    return new DateTime(year, month, day);
}

Func<int, short, byte, DateTime> f = GetDate;
Func<int, Func<short, Func<byte, DateTime>>> curry = f.Curry();
Func<int, short, byte, DateTime> uncurry = curry.Uncrry();

var result1 = curry(2018)(2)(28);
var result2 = uncurry(2018, 2, 28);

遅延評価

引数を与えてFunc<T1, T2, T3, TResult>Func<TResult>に変換します。引数を与えるだけで実行はされません。

遅延評価
/// <summary>
/// Converts the specified delegate to a lasy evaluation delegate.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="func"></param>
/// <param name="arg1"></param>
/// <param name="arg2"></param>
/// <returns></returns>
public static Func<TResult> Lazy<T1, T2, TResult>(this Func<T1, T2, TResult> func, T1 arg1, T2 arg2)
{
    return () => func(arg1, arg2);
}

// 例
internal static DateTime GetDate(int year, short month, byte day)
{
    return new DateTime(year, month, day);
}

Func<int, short, byte, DateTime> f = GetDate;
Func<DateTime> lazy = f.Lazy(2018, 2, 28);

var result1 = GetDate(2018, 2, 28);
var result2 = lazy();

部分適用

先頭の引数を与えてFunc<T1, T2, T3, TResult>Func<T2, T3, TResult>に変換します。引数を与えるだけで実行はされません。

部分適用
/// <summary>
/// Converts the specified delegate to a partial apply delegate.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="func"></param>
/// <param name="arg1"></param>
/// <returns></returns>
public static Func<T2, T3, TResult> Partial<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func, T1 arg1)
{
    return (arg2, arg3) => func(arg1, arg2, arg3);
}

// 例
internal static DateTime GetDate(int year, short month, byte day)
{
    return new DateTime(year, month, day);
}

Func<int, short, byte, DateTime> f = GetDate;
Func<short, byte, DateTime> applyYear = f.Partial(2018);

var result1 = GetDate(2018, 2, 28);
var result2 = applyYear(2, 28);

カリー化後の部分適用

Func<T1, Func<T2, Func<T3, TResult>>>に対して何れかの引数を与えて型引数の数を減らします。カリー化解除と共に使うことが多いのではないかと思います。

カリー化後の部分適用
/// <summary>
/// Converts the specified curried delegate to a partial apply delegate.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public static Func<T2, Func<T3, Func<T1, TResult>>> Partial<T1, T2, T3, TResult>(this Func<T1, Func<T2, Func<T3, TResult>>> func)
{
    return arg2 => arg3 => arg1 => func(arg1)(arg2)(arg3);
}

// 例
internal static DateTime GetDate(int year, short month, byte day)
{
    return new DateTime(year, month, day);
}

Func<int, short, byte, DateTime> f = GetDate;
Func<int, Func<short, Func<byte, DateTime>>> curry = f.Curry();
Func<int, Func<short, DateTime>> applyDay = curry.Partial().Partial()(28);
Func<int, short, DateTime> applyDayUncurry = applyDay.Uncurry();

var result1 = GetDate(2018, 2, 28);
var result2 = applyDay(2018)(2);
var result3 = applyDayUncurry(2018, 2);

参考にした記事

【koropicot's blog】C#で部分適用

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2