web-dev-qa-db-ja.com

関数をパラメーターとして取る関数は、それらの関数へのパラメーターもパラメーターとして取る必要がありますか?

データアクセスを簡単に模擬できるため、このような関数を作成していることがよくありますが、パラメーターを受け入れてアクセスするデータを決定するシグネチャを提供しています。

public static string GetFormattedRate(
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

または

public static string GetFormattedRate(
        Func<RateType, string> formatRate,
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = formatRate(rate);
    return formattedRate;
}

次に、次のように使用します。

using FormatterModule;

public static Main()
{
    var getRate = GetRateFunc(connectionStr);
    var formattedRate = GetFormattedRate(getRate, rateType);
    // or alternatively
    var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);

    System.PrintLn(formattedRate);
}

これは一般的な方法ですか?もっと何かをすべきだと思う

public static string GetFormattedRate(
        Func<RateType> getRate())
{
    var rate = getRate();
    return rate.DollarsPerMonth.ToString("C0");
}

しかし、レートタイプごとにメソッドに渡す新しい関数を作成する必要があるため、これはうまく機能しないようです。

ときどきやるべきだと思う

public static string GetFormattedRate(RateType rate)
{
   return rate.DollarsPerMonth.ToString("C0");
}

しかし、それはフェッチとフォーマットの再利用性を奪うようです。フェッチしてフォーマットしたいときはいつでも、1行をフェッチし、もう1行をフォーマットする必要があります。

関数型プログラミングには何が欠けていますか?これは正しい方法ですか、それとも保守と使用の両方が簡単なより良いパターンがありますか?

20
rushinge

これを十分に長く行うと、最終的にこの関数を何度も何度も作成するようになります。

public static Type3 CombineFunc1AndFunc2(
    Func<Type1, Type2> func1,
    Func<Type2, Type3>> func2,
    Type1 input)
{
    return func2(func1(input))
}

おめでとうございます、あなたは 関数構成 を発明しました。

このようなラッパー関数は、1つのタイプに特化している場合はあまり役に立ちません。ただし、いくつかの型変数を導入して入力パラメーターを省略すると、GetFormattedRate定義は次のようになります。

public static Func<A, C> Compose(
    Func<B, C> outer, Func<A, B>> inner)
{
    return (input) => outer(inner(input))
}

var GetFormattedRate = Compose(FormatRate, GetRate);
var formattedRate = GetFormattedRate(rateKey);

現状では、あなたがやっていることにはほとんど意味がありません。これは一般的なものではないため、そのコードをあちこちに複製する必要があります。これで、コードは非常に複雑になります。これは、コードが、1,000個の小さな関数から必要なものをすべて自分でアセンブルする必要があるためです。しかし、あなたの心は正しい場所にあります。物事を組み合わせるために、これらの種類の一般的な高次関数を使用することに慣れる必要があります。または、古き良きファッションラムダを使用してFunc<A, B>およびAFunc<B>に変換します。

自分を繰り返さないでください。

38
Jack

関数とそのパラメーターを渡す理由はまったくありません。それらのパラメーターを指定して呼び出すだけです。実際、あなたの場合、あなたは関数を渡す理由はありませんまったく。呼び出し元は、関数自体を呼び出して結果を渡すこともできます。

それについて考えてください-代わりに:

_var formattedRate = GetFormattedRate(getRate, rateType);
_

なぜ単に使用しないのですか?

_var formattedRate = GetFormattedRate(getRate(rateType));
_

不要なコードを減らすだけでなく、結合も減らします-レートのフェッチ方法を変更したい場合(たとえば、getRateに2つの引数が必要になった場合)、GetFormattedRateを変更する必要はありません。

同様に、GetFormattedRate(formatRate, getRate, rateKey)と書く代わりにformatRate(getRate(rateKey))と書く理由はありません。

物事を複雑にしないでください。

106
user253751

関数に関数を渡す必要がある場合は、追加の引数を渡すか、ループでそれを呼び出すため、代わりにラムダを渡すことができます。

public static string GetFormattedRate(
        Func<string> getRate)
{
    var rate = getRate();
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

var formattedRate = GetFormattedRate(()=>getRate(rateKey));

ラムダは、関数が認識していない引数をバインドし、それらが存在することさえ隠します。

15
ratchet freak