Le funzioni che assumono funzioni come parametri, assumono anche parametri come funzioni di tali parametri?

19

Spesso mi trovo a scrivere funzioni che sembrano così perché loro permettimi di semplificare facilmente l'accesso ai dati e di fornire comunque una firma che accetti i parametri per determinare quali dati accedere.

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

o

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

Quindi lo uso in questo modo:

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

Questa è una pratica comune? Mi sento come se dovessi fare qualcosa di più simile

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

Ma non sembra funzionare molto bene perché dovrei fare una nuova funzione per passare al metodo per ogni tipo di tariffa.

A volte mi sento come dovrei fare

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

Ma ciò sembra togliere qualsiasi possibilità di recupero e formattazione. Ogni volta che voglio recuperare e formattare devo scrivere due righe, una da scaricare e una da formattare.

Cosa mi manca della programmazione funzionale? È questo il modo giusto per farlo, o esiste un modello migliore che sia facile da mantenere e utilizzare?

    
posta rushinge 21.03.2017 - 00:11
fonte

4 risposte

40

Se lo fai abbastanza a lungo, alla fine ti troverai a scrivere questa funzione più e più volte:

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

Congratulazioni, hai inventato la composizione delle funzioni .

Le funzioni wrapper come questa non hanno molto senso quando sono specializzate per un tipo. Tuttavia, se si introducono alcune variabili di tipo e si omette il parametro di input, la definizione GetFormattedRate ha il seguente aspetto:

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

Così com'è, quello che stai facendo ha poco scopo. Non è generico, quindi è necessario duplicare quel codice dappertutto. Sovracompone il tuo codice perché ora il tuo codice deve assemblare tutto ciò di cui ha bisogno da migliaia di piccole funzioni da solo. Tuttavia, il tuo cuore è nel posto giusto: devi solo abituarti a utilizzare questi tipi di funzioni di ordine superiore generiche per mettere insieme le cose. Oppure usa un buon lambda vecchio stile per trasformare Func<A, B> e A in Func<B> .

Non ripeterti.

    
risposta data 21.03.2017 - 00:29
fonte
108

Non c'è assolutamente alcun motivo per passare una funzione e i suoi parametri, per poi chiamarla con quei parametri. In effetti, nel tuo caso non hai motivo di passare una funzione affatto . Il chiamante potrebbe anche chiamare la funzione stessa e passare il risultato.

Pensaci - invece di usare:

var formattedRate = GetFormattedRate(getRate, rateType);

perché non usare semplicemente:

var formattedRate = GetFormattedRate(getRate(rateType));

Oltre a ridurre il codice non necessario riduce anche l'accoppiamento - se vuoi cambiare il modo in cui viene prelevata la velocità (ad esempio, se getRate ora ha bisogno di due argomenti) non devi cambiare GetFormattedRate .

Allo stesso modo, non c'è motivo di scrivere GetFormattedRate(formatRate, getRate, rateKey) invece di scrivere formatRate(getRate(rateKey)) .

Non complicare eccessivamente le cose.

    
risposta data 21.03.2017 - 00:22
fonte
15

Se hai assolutamente bisogno di passare una funzione nella funzione perché passa qualche argomento in più o la chiama in un ciclo, puoi invece passare un lambda:

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

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

Il lambda legherà gli argomenti a cui la funzione non sa e nasconderà che esistono persino.

    
risposta data 21.03.2017 - 12:52
fonte
-1

Non è questo quello che vuoi?

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

E poi chiamalo così:

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

        System.PrintLn(formattedRate);
    }
}

Se si desidera un metodo che può comportarsi in molti modi diversi in un linguaggio orientato agli oggetti come C #, il modo usuale per farlo è di chiamare il metodo un metodo astratto. Se non hai una ragione specifica per farlo in un modo diverso, dovresti farlo in questo modo.

Ti sembra una buona soluzione, o ci sono aspetti negativi a cui stai pensando?

    
risposta data 22.03.2017 - 02:33
fonte

Leggi altre domande sui tag