È sconsigliabile creare una funzione che rinomini essenzialmente una funzione incorporata?

40

Mi confondo con le funzioni minime e massime, in determinati contesti.

In un contesto, quando si utilizzano le funzioni per prendere il maggiore o il minore tra due valori, non vi è alcun problema. Ad esempio,

//how many autographed CD's can I give out?
int howManyAutographs(int CDs, int Cases, int Pens)
{
    //if no pens, then I cannot sign any autographs
    if (Pens == 0)
        return 0;

    //I cannot give away a CD without a case or a case without a CD
    return min(CDs, Cases);
}

Facile. Ma in un altro contesto, mi confondo. Se sto cercando di impostare un massimo o un minimo, lo faccio tornare indietro.

//return the sum, with a maximum of 255
int cappedSumWRONG(int x, int y)
{
    return max(x + y, 255); //nope, this is wrong
}

//return the sum, with a maximum of 255
int cappedSumCORRECT(int x, int y)
{
    return min(x + y, 255); //much better, but counter-intuitive to my mind
}

È sconsigliabile creare le mie funzioni come segue?

//return x, with a maximum of max
int maximize(int x, int max)
{
    return min(x, max);
}

//return x, with a minimum of min
int minimize(int x, int min)
{
    return max(x, min)
}

Ovviamente, usare i builtin sarà più veloce ma mi sembra una microottimizzazione inutile. C'è qualche altra ragione per cui sarebbe sconsigliabile? Che ne dici di un progetto di gruppo?

    
posta Devsman 08.06.2016 - 20:35
fonte

15 risposte

120

Come altri hanno già detto: non creare una funzione con un nome simile a quello di una libreria incorporata, una libreria standard o una funzione generalmente utilizzata, ma modificarne il comportamento. È possibile abituarsi a una convenzione di denominazione anche se a prima vista non ha molto senso ma sarà impossibile ragionare sul funzionamento del codice una volta introdotte quelle altre funzioni che fanno la stessa cosa ma che hanno i loro nomi sono scambiati.

Invece di "sovraccaricare" i nomi usati dalla libreria standard, usa nuovi nomi che trasmettano esattamente ciò che intendi. Nel tuo caso, non sei davvero interessato a un "minimo". Piuttosto, vuoi cap un valore. Matematicamente, questa è la stessa operazione ma semanticamente non è abbastanza. Quindi, perché non solo una funzione

int cap(int value, int limit) { return (value > limit) ? limit : value; }

fa ciò che è necessario e lo dice dal suo nome. (Puoi anche implementare cap in termini di min come mostrato in timster 's answer ).

Un altro nome di funzione utilizzato di frequente è clamp . Richiede tre argomenti e "blocca" un valore fornito nell'intervallo definito dagli altri due valori.

int clamp(int value, int lower, int upper) {
    assert(lower <= upper);  // precondition check
    if (value < lower) return lower;
    else if (value > upper) return upper;
    else return value;
}

Se stai usando un nome di funzione così conosciuto, qualsiasi nuova persona che si unisce al tuo team (incluso il futuro che torni al codice dopo un po ') capirà rapidamente cosa sta succedendo invece di maledirti per averli confusi rompendo le loro aspettative sui nomi di funzioni che pensavano di conoscere.

    
risposta data 08.06.2016 - 22:16
fonte
115

Se crei una funzione del genere in cui minimize(4, 10) restituisce 10 , direi che non è consigliabile perché i tuoi colleghi programmatori potrebbero strangolarti.

(Ok, forse non ti strangolano letteralmente a morte, ma sul serio ... Non farlo.)

    
risposta data 08.06.2016 - 20:42
fonte
26

L'aliasing di una funzione va bene, ma non provare a cambiare il significato dei termini esistenti

È corretto creare un alias della funzione - librerie comuni fallo sempre .

Tuttavia, è una cattiva idea usare i termini in modo contrario all'utilizzo comune, come il tuo esempio in cui la tua mente massima e minima dovrebbero essere capovolte. È confuso con gli altri programmatori e ti renderai un disservizio addestrandoti a continuare a interpretare questi termini in un modo non standard.

Quindi nel tuo caso, abbandona la parlata "mininum / maximum" che trovi confusa e crea il tuo codice, facile da capire.

Rifacimento del tuo esempio:

int apply_upper_bound(int x, int y)
{
    return min(x, y);
}


int apply_lower_bound(int x, int y)
{
    return max(x, y)
}

Come bonus aggiuntivo, ogni volta che guardi questo codice, ti ricorderai come min e max sono usati nel tuo linguaggio di programmazione. Alla fine, avrà un senso nella tua testa.

    
risposta data 08.06.2016 - 21:46
fonte
12

Adoro questa domanda. Scomporlo però.

1: Dovresti racchiudere una singola riga di codice?

Sì, posso pensare a molti esempi in cui potresti farlo. Forse stai applicando parametri digitati o nascondendo un'implementazione concreta dietro un'interfaccia. Nel tuo esempio stai essenzialmente nascondendo una chiamata al metodo statico.

In questo momento puoi fare un sacco di cose su una singola riga.

2: I nomi "Min" e "Max" confondono

Sì! Sono totalmente! Un guru di codifica pulito li rinominerebbe "FunctionWhichReturnsTheLargestOfItsParameters" o qualcosa del genere. Fortunatamente abbiamo documentazione e (se siete fortunati) IntelliSense e commenti per aiutarci così chiunque sia confuso da i nomi possono leggere su cosa dovrebbero fare.

3: dovresti rinominarli per qualcos'altro da solo.

Sì, fallo. Ad esempio, potresti avere:

class Employee
{
    int NumberOfHolidayDaysIShouldHave(int daysInLue, int maxAllowableHolidayDays)
    {
         // Return the number of days in lue, but keep the value under the max allowable holiday days!
         // Don't use max, you fool!!
         return Math.Max(daysInLue, maxAllowableHolidayDays)
    }
}

Aggiunge un significato e il chiamante non deve o desidera sapere come calcolare il valore.

4: dovresti rinominare "min" in "maxim"

No !! sei pazzo ?! Ma sì, la domanda sottolinea il fatto che persone diverse leggono significati diversi in nomi di funzioni e oggetti. Ciò che una persona trova chiara e convenzionale un'altra trova opaca e confusa. Questo è il motivo per cui abbiamo commenti. Dovresti invece scrivere:

// Add x and y, but don't let it go over 255
s = min(x + y, 255);

Poi quando qualcuno legge

// Add x and y, but don't let it go over 255
s = max(x + y, 255);

sanno che hai fatto un errore.

    
risposta data 08.06.2016 - 22:02
fonte
4

No . Non creare funzioni con nomi molto simili alle funzioni integrate, ma che in realtà fa l'opposto . Può sembrare intuitivo per te, ma sarà molto confuso con gli altri sviluppatori e persino con te stesso in futuro quando avrai più esperienza.

Il significato di max è "il massimo di", ma la tua comprensione "intuitiva" è qualcosa come "al massimo". Ma questa è semplicemente una comprensione errata della funzione, e la modifica del nome da max a maximum non comunica la tua diversa interpretazione. Anche se credi fermamente che i progettisti di linguaggi abbiano fatto un errore, non fare qualcosa di simile.

Ma cambiare il nome per dire cap(x, limit) come è stato suggerito andrebbe bene, dal momento che comunica chiaramente l'intenzione, anche se avvolge solo min .

    
risposta data 09.06.2016 - 16:59
fonte
3

Ciò che potrebbe creare confusione è usare Capped nel nome della tua funzione o capire cosa significa mettere un cap. È un limitatore e non richiede un massimo di nulla.

Se ti viene chiesto il più basso, il più piccolo o il più iniziale, ritieni che Max sia la funzione appropriata?

Lascia il minimo e il massimo da soli. Scrivi test così almeno lo avrai corretto la seconda volta.

Se ti viene richiesto di utilizzare queste funzioni così tanto nel tuo progetto, otterrai una sorta di suggerimento per aiutarti a chiarire quale usare. Un po 'come < o & gt ;, la parte larga della bocca è rivolta verso il valore maggiore.

    
risposta data 08.06.2016 - 21:19
fonte
2

Per rispondere alla tua domanda: C'è qualche altra ragione per cui sarebbe sconsigliabile? Che ne dici di un progetto di gruppo? Ha senso che tu voglia le tue funzioni, il che non è un problema. Assicurati che siano nella tua classe di supporto e non siano facilmente chiamabili per gli altri a meno che non lo importino. (Joes.Utilities.)

Ma per guardare di nuovo al tuo problema, in pratica penserei invece:

return (input >= 255) ? 255 : input;

Ti stai confondendo perché stai provando ad applicare la tua logica cerebrale a queste funzioni min / max. Invece parla solo in inglese. if il input è greater than or equal to 255 then return 255 altrimenti return il input .

Che è:

if (input >= 255) {
   255 
} else {
   input
}

La mia opinione. Stai andando per le funzioni max \ min per i motivi sbagliati, la velocità di queste cose è trascurabile. Fai ciò che ha senso.

    
risposta data 09.06.2016 - 07:26
fonte
1

Pur comprendendo il tuo problema, sarei riluttante a farlo. Sarebbe meglio semplicemente perforare nel tuo cranio ciò che min () e max () fanno.

La maggior parte dei programmatori sa quali sono le funzioni min () e max () - anche se, come te, a volte hanno problemi con la loro intuizione su cui usare in un dato momento. Se sto leggendo un programma e vedo max (x, y), so immediatamente cosa fa. Se crei la tua funzione "alias", chiunque altro leggendo il tuo codice non saprà cosa fa questo alias. Devono trovare la tua funzione. Rompe inutilmente il flusso di lettura e costringe il lettore a fare qualche riflessione in più per capire il tuo programma.

Se hai problemi a capire quale usare a un certo punto, direi, aggiungi un commento che lo spieghi. Quindi, se un futuro lettore è confuso allo stesso modo, il tuo commento dovrebbe chiarirlo. Oppure se lo fai male ma il commento spiega cosa stavi cercando di fare, la persona che prova a eseguire il debug avrà un indizio.

Una volta che si alias una funzione perché il nome si scontra con l'intuizione ... è questo l'unico caso in cui questo è un problema? O stai per alias altre funzioni? Forse sei confuso da "leggere" e trovi più facile pensarlo come "accetta", cambia "aggiungi" a "StringTogether", "round" a "DropDecimals", ecc. Ecc. Porta questo a un ridicolo estremo e i tuoi programmi saranno incomprensibili.

In effetti, anni fa ho lavorato con un programmatore a cui non piaceva tutta la punteggiatura di C. Quindi scrisse un sacco di macro per fargli scrivere "THEN" invece di "{" e "END-IF" invece di "}" e dozzine di altre tali sostituzioni. Quindi quando hai provato a leggere i suoi programmi, non assomigliava più a C, era come dover imparare una nuova lingua. Non ricordo ora se "AND" è stato tradotto in "&" o "& &" - e questo è il punto. Minacci l'investimento che le persone hanno fatto nell'apprendimento della lingua e della biblioteca.

Detto questo, non direi che una funzione che non fa altro che chiamare una funzione di libreria standard è necessariamente negativa. Se il punto della tua funzione non è quello di creare un alias, ma di incapsulare un comportamento che sembra essere una singola funzione, questo potrebbe essere buono e corretto. Voglio dire, se logicamente e inevitabilmente devi fare un massimo a questo punto nel programma, quindi chiama semplicemente max direttamente. Ma se devi eseguire dei calcoli che oggi richiedono un massimo, ma potrebbe essere modificato in futuro per fare qualcos'altro, allora è appropriata una funzione intermedia.

    
risposta data 12.06.2016 - 08:07
fonte
0

È possibile rinominare le funzioni incorporate, a condizione che i nuovi nomi rendano il codice molto chiaro e che nessuno possa capirlo. (Se stai usando C / C ++ non usa #define in quanto rende difficile vedere cosa sta succedendo.) Il nome di una funzione dovrebbe comportarsi come un commento che spiega cosa sta facendo il codice chiamante e perché lo sta facendo .

Non sei l'unica persona che ha avuto questo problema con min e max, tuttavia devo ancora vedere una buona soluzione generale che funziona su tutti i domini. Penso che un problema con la denominazione di queste funzioni sia che i due argomenti hanno significati logici diversi, ma sono presentati come aventi lo stesso significato.

Se la tua lingua lo consente, puoi provare

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)
    
risposta data 09.06.2016 - 11:00
fonte
0

Non ci sono.

Non scrivi i tuoi wrapper. I nomi di quei wrapper non sono molto significativi.

Quello che stai cercando di fare è una confusione di codice gentile. Stai inventando un ulteriore livello che ha 2 scopi:

  1. Gli altri non possono capire il tuo codice.
  2. Non impari mai a capire il codice degli altri.

Nascondendo cose che non ti piacciono, stai solo ferendo il tuo codice ora e te stesso in futuro. Non puoi crescere rimanendo nella tua zona di comfort. Quello che ti serve è sapere come funzionano min e max .

    
risposta data 10.06.2016 - 14:09
fonte
-1

È Ok, e non è davvero controintuitivo usare Min, Max per scavare sopra e sotto. Anche questo è fatto usando:

  • floor () e ceil ()
  • morsetto, limite, limiti
  • e ovviamente mod con troncamento di alto livello.

Nel firmware risale a oltre MMX, che a sua volta precede la grafica 3D moderna che si basa su questo estensivo.

Sostituire una funzione standard del settore anche localmente mi preoccuperebbe, un nome derivativo potrebbe essere migliore. Forse gli studenti C ++ potrebbero sovraccaricare per la loro classe oscura.

    
risposta data 09.06.2016 - 04:04
fonte
-1

In alcuni casi va bene, ma non nel tuo esempio, perché ci sono molti modi migliori per esprimerlo:
saturate , clamp , clip , ecc.

    
risposta data 09.06.2016 - 05:39
fonte
-1

Preferirei creare una funzione generica denominata 'bounded'

//Assumes lower_bound <= upper_bound
template <typename T>
T bounded(T value, T lower_bound, T upper_bound){
    if (value < lower_bound)
        return lower_bound;
    if (value > upper_bound)
        return upper_bound;
    return value;
}

//Checks an upper (by default) or lower bound
template <typename T>
T bounded(T value, T bound, bool is_upper_bound = true){
    if (is_upper_bound){
        if (value > bound)
            return bound;
    }
    else {
        if (value < bound)
            return bound;
    }
    return value;
}

o con l'uso di "min" e "max"

//Assumes lower_bound <= upper_bound
template <typename T>
T bounded(T value, T lower_bound, T upper_bound){
    return max(min(value, upper_bound), lower_bound);
}

//Checks an upper (by default) or lower bound
template <typename T>
T bounded(T value, T bound, bool is_upper_bound = true){
    if (is_upper_bound)
        return min(value, bound);
    else
        return max(value, bound);
}
    
risposta data 09.06.2016 - 14:58
fonte
-1

Che ne dici di chiamare le tue funzioni:

atmost(x,255) : restituisce al massimo x o 255 al massimo.

atleast(10,x) : restituisce il più alto di x o almeno 10.

    
risposta data 10.06.2016 - 13:10
fonte
-1

min(x+y, MAX_VALUE); avrebbe molto più significato di myCustomFunction(x, y);

Quindi la risposta è SI, è sconsigliabile . Serve solo come un alias per il tuo linguaggio del cervello.

    
risposta data 09.06.2016 - 03:23
fonte

Leggi altre domande sui tag