함수를 매개 변수로 사용하는 함수도 해당 함수에 매개 변수를 매개 변수로 사용해야합니까?


20

데이터 액세스를 쉽게 조롱하고 액세스 할 데이터를 결정하기 위해 매개 변수를 허용하는 서명을 제공하기 때문에 이와 같은 함수를 작성하는 경우가 많습니다.

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");
}

그러나 그것은 모든 가져 오기 및 형식 재사용 성을 없애는 것처럼 보입니다. 페치 및 포맷을 원할 때마다 하나는 페치 및 하나는 포맷하는 두 줄을 작성해야합니다.

함수형 프로그래밍에서 무엇을 놓치고 있습니까? 이것이 올바른 방법입니까, 유지 관리하고 사용하기 쉬운 더 나은 패턴이 있습니까?


50
DI 암은 지금까지 퍼졌습니다.
Idan Arye

16
나는 왜이 구조가 처음에 사용되는지보기 힘들다. 확실히 더 편리 (와의 명확한 용) GetFormattedRate()이 기능을 수용 시키도록하는 것과는 반대로, 매개 변수로 형식으로 속도를 받아들이 반환하는 매개 변수로 형식으로 속도?
aroth

6
더 좋은 방법은 closures매개 변수 자체를 함수에 전달하는 위치를 사용하는 것입니다 . 결과적으로 해당 매개 변수를 참조하는 함수가 제공됩니다. 이 "구성된"기능은이를 사용하는 기능에 매개 변수로 전달됩니다.
토마스 정크

7
@IdanArye DI 암?
Jules

11
@ 줄스 의존성 주입 암
고양이

답변:


39

이 작업을 충분히 오래하면 결국이 함수를 반복해서 작성하게됩니다.

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

축하합니다 . 함수 구성을 발명했습니다 .

이와 같은 래퍼 함수는 한 유형에 특화된 경우 많이 사용되지 않습니다. 그러나 일부 유형 변수를 도입하고 입력 매개 변수를 생략하면 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);

그것이 말하듯이, 당신이하고있는 일은 거의 목적이 없습니다. 그것은 일반적인 것이 아니므로 그 코드를 모든 곳에서 복제해야합니다. 이제 코드가 수천 개의 작은 함수 자체로 필요한 모든 것을 조립해야하기 때문에 코드가 너무 복잡해집니다. 당신의 마음은 올바른 위치에 있습니다 : 당신은 이런 종류의 일반적인 고차 함수 를 사용하여 일을 정리 하는 데 익숙해 져야 합니다. 또는 설정하는 좋은 오래된 패션 람다 사용 Func<A, B>A로를 Func<B>.

반복하지 마십시오.


16
반복하지 않으면 코드가 악화 될 경우 반복하십시오. 예를 들어 항상 대신 두 줄을 쓰는 것처럼 FormatRate(GetRate(rateKey)).
user253751

6
@immibis 나는 그가 GetFormattedRate지금부터 직접 사용할 수 있다고 생각합니다 .
Carles

나는 이것이 내가 여기서하려고하는 것이라고 생각하고, 나는이 Compose 기능을 전에 시도했지만 내 두 번째 기능에는 종종 하나 이상의 매개 변수가 필요하기 때문에 거의 사용하지 않는 것 같습니다. 아마도이 작업은 @ thomas-junk
rushinge

@rushinge 이러한 종류의 구성은 항상 단일 인수를 갖는 전형적인 FP 함수에서 작동합니다 (추가 인수는 실제로 자체의 함수입니다 Func<Func<A, B>, C>. 이는 모든 기능에 대해 작동하는 하나의 작성 기능 만 필요함을 의미합니다. 그러나 클로저를 사용하는 것만으로 C # 함수를 충분히 사용할 수 있습니다. 전달하는 대신 Func<rateKey, rateType>실제로 필요 Func<rateType>하며 func를 전달할 때처럼 빌드합니다 () => GetRate(rateKey). 요점은 대상 함수가 신경 쓰지 않는 인수를 노출시키지 않는다는 것입니다.
Luaan

1
@immibis 예, Compose함수는 자체 선택 속도를 제공하는 함수 GetRate에 전달하려는 경우 ( Compose(FormatRate, GetRate)예 : 명부.
jpaugh

107

함수와 매개 변수를 전달할 이유가 없으며, 그런 다음 해당 매개 변수로 호출해야합니다. 실제로, 귀하의 경우에는 함수 를 전혀 전달할 이유가 없습니다 . 호출자는 함수 자체를 호출하고 결과를 전달할 수도 있습니다.

다음을 사용하는 대신 생각해보십시오.

var formattedRate = GetFormattedRate(getRate, rateType);

왜 간단하게 사용하지 않습니까?

var formattedRate = GetFormattedRate(getRate(rateType));

?

불필요한 코드를 줄이는 것뿐만 아니라 결합도 줄입니다. 요율을 가져 오는 방법을 변경하려면 (예 : getRate두 개의 인수가 필요한 경우 ) 변경할 필요가 없습니다 GetFormattedRate.

마찬가지로 GetFormattedRate(formatRate, getRate, rateKey)writing 대신 쓸 이유가 없습니다 formatRate(getRate(rateKey)).

지나치게 복잡하지 마십시오.


3
이 경우에 당신은 맞습니다. 그러나 내부 함수가 루프 또는 맵 함수와 같이 여러 번 호출되면 인수를 전달하는 기능이 유용합니다. 또는 @Jack answer에서 제안한 기능 구성 / 카레를 사용하십시오.
user949300

15
@ user949300은 가능하지만 OP의 사용 사례는 아닙니다 (그렇다면 formatRate형식화해야 할 요율에 따라 매핑되어야합니다).
jonrsharpe

4
@ user949300 언어가 클로저를 지원하지 않거나 람다가 입력하기가
어려울 때만

4
함수와 매개 변수를 다른 함수에 전달하는 것은 게으른 의미가없는 언어에서 평가를 지연 시키는 완전히 유효한 방법입니다. en.wikipedia.org/wiki/Thunk
Jared Smith

4
@JaredSmith 언어가 클로저를 지원하지 않는 경우에만 매개 변수를 전달하여 함수를 전달합니다.
user253751

15

추가 인수를 전달하거나 루프에서 호출하기 때문에 함수에 함수를 절대 전달해야하는 경우 대신 람다를 전달할 수 있습니다.

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

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

람다는 함수가 알지 못하는 인수를 바인딩하고 심지어 존재한다는 것을 숨길 것입니다.


-1

이것이 당신이 원하는 것이 아닙니까?

class RateFormatter
{
    public abstract RateType GetRate(string rateKey);

    public abstract string FormatRate(RateType rate);

    public string GetFormattedRate(string rateKey)
    {
        var rate = GetRate(rateKey);
        var formattedRate = FormatRate(rate);
        return formattedRate;
    }
}

그런 다음 다음과 같이 호출하십시오.

static class Program
{
    public static void Main()
    {
        var rateFormatter = new StandardRateFormatter(connectionStr);
        var formattedRate = rateFormatter.GetFormattedRate(rateKey);

        System.PrintLn(formattedRate);
    }
}

C #과 같은 객체 지향 언어에서 여러 가지 다른 방식으로 동작 할 수있는 메소드를 원할 경우 일반적인 방법은 메소드를 추상 메소드로 호출하는 것입니다. 다른 방법으로해야 할 특별한 이유가 없다면 그렇게해야합니다.

이것이 좋은 해결책처럼 보입니까, 아니면 당신이 생각하는 단점이 있습니까?


1
답에 몇 가지 이상한 것들이 있습니다 (포매터가 포맷터 인 경우 왜 포맷터가 속도를 얻습니까? GetFormattedRate메서드를 제거하고 호출 할 수도 있습니다 IRateFormatter.FormatRate(rate)). 그러나 기본 개념은 정확하며 너무 많은 형식 지정 방법이 필요한 경우 OP가 코드 다형성을 구현해야한다고 생각합니다.
BgrWorker
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.