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


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.