È sconsigliabile creare una funzione che rinomina essenzialmente una funzione integrata?


40

Mi confondo sulle funzioni min e max, in determinati contesti.

In un contesto, quando si utilizzano le funzioni per accettare il maggiore o il minore di due valori, non vi è alcun problema. Per 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 ottengo 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 svolgere 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 a me sembra una inutile microottimizzazione. C'è qualche altro motivo per cui questo sarebbe sconsigliabile? Che dire di un progetto di gruppo?


72
Se min e max danneggiano la tua leggibilità, prendi in considerazione la possibilità di scambiarlo, quindi sostituiscilo con "se". A volte vale la pena scrivere un po 'più di codice per avere una migliore leggibilità.
T. Sar - Ripristina Monica l'

23
con un breve post hai distrutto l'intero concetto di "codice pulito". Ti saluto signore!
Ewan,

21
Forse si può considerare il 11 C ++ std::clampfunzione o qualcosa di simile.
rwong,

15
Forse i nomi migliori sarebbero up_to(per min) e at_least(per max)? Penso che trasmettano il significato meglio di minimize, ecc. Anche se potrebbe volerci un attimo per capire perché sono commutativi.
Warbo,

59
mine maxanche minimizee maximizesono nomi totalmente sbagliati per le funzioni che vuoi scrivere. L'impostazione predefinita mine ha maxmolto più senso. In realtà QUASI hai i nomi delle funzioni giusti. Questa operazione si chiama bloccaggio o tappatura e sono state scritte due funzioni di tappatura. Suggerirei capUpperBounde capLowBound. Non devo spiegare a nessuno quale fa, è ovvio.
Slebetman,

Risposte:


120

Come altri hanno già detto: non creare una funzione con un nome simile a quello di una funzione incorporata, standard-library o generalmente usata ma cambiarne 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 hanno i loro nomi sono stati scambiati.

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

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

fa quello che è necessario e lo dice dal suo nome. (Si potrebbe anche implementare capin termini di mincome mostrato nella timster 's risposta ).

Un altro nome di funzione usato di frequente è clamp. Prende 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ì generalmente noto, 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 il loro aspettative sui nomi delle funzioni che pensavano di conoscere.


2
Potresti anche chiamarlo getValueNotBiggerThan (x, limit) questo è l'approccio giusto, e con un compilatore decente la funzione sarà incorporata e il codice macchina risultante sarà esattamente lo stesso dell'utilizzo dei built-in
Falco

20
@Falco Direi che "cap" è quasi un sinonimo di "ottenere un valore non superiore a" ma non ho intenzione di dipingere quel capannone della bici oggi. ;-)
5gon12eder

1
clampè ampiamente utilizzato in tutti i tipi di elaborazione del segnale e operazioni simili (elaborazione di immagini, ecc.), quindi è sicuramente quello che vorrei fare anch'io. Anche se non direi che richiede limiti superiori e inferiori: l'ho visto abbastanza spesso anche per una sola direzione.
Voo,

1
Ne parlerò anche io clamp. E se è scritto correttamente, puoi semplicemente usare un limite di infinito / infinito negativo per quando lo desideri solo in un modo. Ad esempio, per assicurarti che un numero non sia maggiore di 255 (ma senza limite inferiore), dovrai utilizzare clamp(myNumber, -Infinity, 255).
Kat

115

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

(Va bene, forse non ti strangoleranno letteralmente a morte, ma sul serio ... Non farlo.)


2
Inoltre è del tutto possibile che quando torni a lavorare su questo codice tra qualche anno, tu stesso passerai diverse ore a cercare di capire perché le tue cifre sono sbagliate quando chiami minimizza ...
Paddy

Aww ... questa risposta aveva un commento davvero interessante (e abbastanza votato). (È stato anche il primo commento sulla risposta, che ha aiutato il flusso umoristico.)
TOOGAM

Continuo a voler ottenere schede perforate e barrare il simbolo DO NOT(da "NON mandare, piegare o mutilare"). Qualcuno che ha implementato qualcosa del genere avrebbe ricevuto una carta.
Clockwork-Muse

26

Alias ​​di una funzione va bene, ma non provare a cambiare il significato dei termini esistenti

È corretto creare un alias della funzione: le librerie comuni lo fanno sempre .

Tuttavia, è una cattiva idea usare i termini in modo contrario all'utilizzo comune, come ad esempio il tuo esempio dove dovrebbero essere capovolti max e min. È fonte di confusione per gli altri programmatori e farai un disservizio allenandoti a continuare a interpretare questi termini in modo non standard.

Quindi, nel tuo caso, abbandona il linguaggio "minimo / massimo" che trovi confuso e crea il tuo codice facile da capire.

Rifattorizzare il 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 guarderai questo codice, ti ricorderai come vengono usati min e max nel tuo linguaggio di programmazione. Alla fine, avrà senso nella tua testa.


4
Penso che tu abbia frainteso l'applicazione mine maxche confonda l'OP. È quando minviene utilizzato per impostare un limite superiore fisso su un valore. get_lower_valuesarebbe altrettanto controintuitivo in questa applicazione. Se dovessi scegliere un nome alternativo per questa operazione, lo chiamerei supremum , anche se non sono sicuro di quanti programmatori lo capiranno immediatamente.
leftaroundabout

3
@leftaroundabout: il supremum dell'insieme {1, 2} è 2, quindi non usare il nome supremumper la funzione get_lower_valuedefinita sopra per chiamare min. Causa al programmatore successivo esattamente lo stesso problema di chiamarlo maximise. Suggerirei di chiamarloapply_upper_bound , ma non sono sicuro che sia perfetto. È ancora strano perché funziona nello stesso modo in cui si inseriscono i parametri, ma il nome implica che uno dei parametri è "il valore" e l'altro è "il limite" e che sono in qualche modo diversi.
Steve Jessop,

1
Penso che essenzialmente ti affidi al lettore per non avere troppa familiarità con la parola supremum, in modo che prendano il tuo significato piuttosto che il significato inglese. Va bene definire il gergo locale nel tuo codice, ma il punto della domanda è cosa fare quando il gergo che vuoi contraddice direttamente un significato già familiare, e temo che tu lo stia ancora facendo (per qualche versione di "inglese" rilevante solo per coloro che hanno studiato materie STEM)
Steve Jessop

3
Questa politica "Aliasing di una funzione va bene, ma non cercare di cambiare il significato dei termini esistenti" è perfetta e magnificamente espressa. In secondo luogo, l'idea che non si debba rinominare in modo confuso in relazione alle funzioni integrate (e: anche se le funzioni incorporate sono scarsamente chiamate / stupide / confuse) è un'idea perfetta. Per quanto riguarda l' esempio max / min che è apparso in questa pagina, è totale confusione eh.
Fattie,

1
OK, ho modificato la risposta per utilizzare "apply_upper_bound" che si adatta sia al ragionamento del PO sia a evitare la terminologia di sovraccarico. La verbosità non è un problema. Non ho intenzione di scrivere il nome del metodo perfetto qui, ho solo fissato alcune regole di base per nominare gli alias. L'OP può modificarlo in qualsiasi cosa ritenga più chiara e concisa.
Tim Grant,

12

Adoro questa domanda. Dividiamolo 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 stai nascondendo un'implementazione concreta dietro un'interfaccia. Nel tuo esempio stai essenzialmente nascondendo una chiamata al metodo statico.

Inoltre, puoi fare molte cose in una sola riga in questi giorni.

2: I nomi 'Min' e 'Max' sono confusi

Sì! Lo sono totalmente! Un guru del codice pulito li rinominerebbe "FunctionWhichReturnsTheLargestOfItsParameters" o qualcosa del genere. Fortunatamente abbiamo documentazione e (se sei fortunato) IntelliSense e commenti per aiutarci in modo che chiunque sia confuso dai nomi possa leggere su ciò che dovrebbero fare.

3: Dovresti rinominarli in qualcos'altro.

Sì, provaci. 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 significato e il chiamante non deve o non vuole sapere come calcolare il valore.

4: Dovresti rinominare "min" per "massimizzare"

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. Ecco perché abbiamo commenti. Dovresti invece scrivere:

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

Quindi 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.


6
Divertente! Nel tuo esempio hai commesso l'errore esatto di cui si occupa l'operazione: vuoi che il numero di festività non sia maggiore di maxHolidaysAllowed, quindi hai bisogno di min (a, b)
Falco

14
Se il codice pulito suggerisce davvero che nomi ridicoli come quelli FunctionWhichReturnsTheLargestOfItsParameterssono una buona cosa, non voglio farne parte.
David Hammen,

1
@Falco lol oops !!!
Ewan,

4
@DavidHammen: non è così, dal momento che nessun vero stile di programmazione mette la verruca FunctionWhichReturnsin primo piano per ogni funzione (che non genera un'eccezione o termina). Potresti finire con getMinimum, getLarger(o getLargestcon più di 2 input), tuttavia, seguendo i consigli reali sulla falsa riga che (a) funzioni pure e / o "getter" dovrebbero usare la verruca get, (b) le parole inglesi non dovrebbero essere abbreviate in nomi. Chiaramente questo è troppo dettagliato per coloro che decidono di chiamare tali funzioni max.
Steve Jessop,

1
sì, vedi il commento di @ falco. non potrei davvero correggerlo dopo quello!
Ewan,

4

No . Non creare funzioni con nomi molto simili alle funzioni incorporate, ma che in realtà fa il contrario . Potrebbe sembrare intuitivo per te, ma sarà molto confuso per altri sviluppatori e anche per te stesso in futuro, quando avrai più esperienza.

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

Ma cambiare il nome per dire cap(x, limit)come è stato suggerito andrebbe bene, poiché comunica chiaramente l'intenzione, anche se si chiude min.


3

Ciò che può confonderti è usare Capped nel nome della tua funzione o la tua comprensione di cosa significhi posizionare un limite. È un limitatore e non richiede un massimo di nulla.

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

Lasciare solo min e max. Scrivi i test in modo che almeno lo otterrai correttamente la seconda volta.

Se ti viene richiesto di utilizzare queste funzioni così tanto nel tuo progetto, ti verrà in mente una sorta di suggerimento che ti aiuterà a chiarire quale utilizzare. In qualche modo simile a <o>, la parte larga della bocca è rivolta verso il valore più grande.


1
+1 per l'eventuale confusione terminologica come problema di fondo. Penso che la confusione inizi nel commento "restituisci la somma, con il massimo di 255", quindi pensa che la maxfunzione sia più appropriata, ma la logica minè la cosa che sta effettivamente cercando.
Revenant,

2

Per rispondere alla tua domanda: c'è qualche altro motivo per cui questo sarebbe sconsigliabile? Che dire di un progetto di gruppo? Ha senso che tu voglia le tue funzioni, il che non è un problema. Assicurati solo che siano nella tua classe di aiuto e non siano facilmente richiamabili per gli altri a meno che non lo importino. (Joes.Utilities.)

Ma per rivedere il tuo problema, in sostanza, invece, penserei:

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

Ti stai confondendo perché stai cercando di applicare la logica del tuo cervello a queste funzioni min / max. Invece parla solo in inglese. ifl' inputè greater than or equal to 255 then return 255altrimenti returnilinput .

Che è:

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

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


1
Uno dei miei colleghi senior diceva sempre "Lascia che il computer pensi a te".

1

Mentre capisco il tuo problema, sarei riluttante a farlo. Sarebbe meglio perforare semplicemente nel tuo cranio cosa fanno min () e max ().

La maggior parte dei programmatori sa cosa fanno le funzioni min () e max () - anche se, come te, a volte lottano con la loro intuizione su quale 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 leggerà il tuo codice non saprà cosa fa questo alias. Devono trovare la tua funzione. Si interrompe inutilmente il flusso di lettura e costringe il lettore a fare qualche pensiero extra per capire il tuo programma.

Se hai difficoltà a capire quale usare ad un certo punto, direi, aggiungi un commento per spiegarlo. Quindi, se un futuro lettore è allo stesso modo confuso, il tuo commento dovrebbe chiarirlo. O se lo fai in modo sbagliato, ma il commento spiega cosa stavi cercando di fare, la persona che cerca di eseguire il debug avrà un indizio.

Una volta che alias una funzione perché il nome si scontra con il tuo intuito ... è l'unico caso in cui questo è un problema? O hai intenzione di alias altre funzioni? Forse sei confuso da "leggi" e trovi più facile pensarlo come "accetta", cambi "append" in "StringTogether", "round" in "DropDecimals", ecc. Ecc. Porta questo ad un estremo ridicolo e i tuoi programmi saranno incomprensibili.

In effetti, anni fa ho lavorato con un programmatore a cui non è piaciuta tutta la punteggiatura in C. Quindi ha scritto un sacco di macro per lasciargli scrivere "THEN" invece di "{" e "END-IF" invece di "}" e dozzine di altre sostituzioni del genere. Quindi, quando hai provato a leggere i suoi programmi, non sembrava nemmeno più C, era come dover imparare una lingua completamente nuova. Non ricordo ora se "AND" tradotto in "&" o "&&" - e questo è il punto. Minerai 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 è creare un alias, ma incapsulare un comportamento che sembra essere una singola funzione, questo potrebbe essere buono e corretto. Voglio dire, se logicamente e inevitabilmente devi fare un max a questo punto nel programma, allora chiama direttamente max. Ma se devi eseguire alcuni calcoli che oggi richiedono un massimo, ma che potrebbero essere modificati in futuro per fare qualcos'altro, allora una funzione intermedia è appropriata.


0

È OK rinominare le funzioni integrate a condizione che i nuovi nomi rendano il tuo codice molto chiaro e non mancherà a nessuno. (Se stai usando C / C ++ non usare un #define perché rende difficile vedere cosa sta succedendo.) Il nome di una funzione dovrebbe agire 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 funzioni in 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 allo stesso modo.

Se la tua lingua lo consente, puoi provare

return  calculatedDiscount.ButNoMoreThen(maxAllowedDiscount)

return  CDsInStocked.ButNoMoreThen(CasesInStock)

0

No.

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

Quello che stai cercando di fare è un tipo di offuscamento del codice. Stai inventando un livello aggiuntivo che ha 2 scopi:

  1. Le altre persone non riescono a capire il tuo codice.
  2. Non impari mai a capire il codice di altre persone.

Nascondendo cose con cui non ti senti a tuo agio, stai solo danneggiando il tuo codice ora e te stesso in futuro. Non puoi crescere rimanendo nella tua zona di comfort. Ciò di cui hai bisogno è imparare come mine come maxlavorare.


-1

Va bene, e non è affatto controintuitivo usare Min, Max per frenare e sparare. Questo viene fatto anche usando:

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

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

Sostituire una funzione standard del settore anche a livello locale mi preoccuperebbe, un nome derivato potrebbe essere migliore. Forse gli studenti C ++ potrebbero sovraccaricare la loro classe oscura.


Mi dispiace, cos'è MMX?
Tobia Tesan,

Stavo pensando a "valori saturi", in cui il risultato è limitato a un limite. L'idea era che il codice avrebbe potuto essere più semplice se non fosse stato necessario includere i limiti e i test di overflow (nella grafica per evitare il ciclismo o il superamento dei limiti negativi). PSUBSB / PSUBSW. Concedo che il meccanismo potrebbe non essere lo stesso, ma l'effetto e l'intento della funzione è.
mckenzm,

-1

Va bene, in alcuni casi, ma non è nel tuo esempio, perché ci sono modi molto migliori per parola è:
saturate, clamp, clip, etc.


-1

Preferirei creare una funzione generica denominata "limitata"

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

-1

Che ne dici di chiamare le tue funzioni:

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

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


-1

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

Quindi la risposta è SÌ, non è consigliabile . Serve solo come alias per il tuo linguaggio del cervello.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.