2012-01-18 5 views
11

Quando si confronta con un numero minimo o massimo di due numeri/funzioni, C# si interrompe se il caso è vero per il primo e implicherebbe la verità per il secondo? Esempi specifici di questi casi sonoFa il confronto con Math.Min o Math.Max ​​cortocircuito?

if(x < Math.Max(y, z())) 

e

if(x > Math.Min(y, z())) 

Da Math.Max(y, z()) restituirà un valore almeno grande come y, se x < y allora non c'è necessità di valutare z(), che potrebbe volerci un po '. Situazione simile con Math.Min.

mi rendo conto che questi potrebbero entrambi essere riscritte lungo le linee di

if(x < y || x < z()) 

per corto-circuito, ma che sia più chiaro quale sia il confronto è senza dover riscrivere. Questo cortocircuito?

+3

Si supponga di chiamare 'if (x> XYZ (y, z()))' Come può il compilatore conoscere il risultato di XYZ? Max, Min, Media o qualcos'altro? –

+0

@ L.B Ottimo punto, non l'avevo considerato in questo modo. – yoozer8

+0

Inoltre, l'espressione "cortocircuito" potrebbe non essere equivalente, a seconda di come 'Min' e' Max' gestiscono NaN. – dan04

risposta

18

Come altri hanno sottolineato, il compilatore non sa nulla della semantica di Min o Max che gli consentirebbe di infrangere la regola che gli argomenti vengono valutati prima che il metodo venga chiamato.

Se si voleva scrivere il proprio, si potrebbe fare in modo abbastanza facilmente:

static bool LazyLessThan(int x, int y, Func<int> z) 
{ 
    return x < y || x < z(); 
} 

e poi chiamarlo

if (LazyLessThan(x, y, z)) 

o

if (LazyLessThan(x, y,()=>z())) 

O per quella materia:

static bool LazyRelation<T>(T x, T y, Func<T> z, Func<T, T, bool> relation) 
{ 
    return relation(x, y) || relation(x, z()); 
} 
... 
if (LazyRelation(x, y,()=>z, (a,b)=> a < b))) 
+0

L'ultimo esempio sarebbe stato ottimo, se fosse possibile aggiungere un argomento predefinito, ad esempio: bool statico LazyRelation (T x, T y, Func x, Func relation = (a, b) => a zmbq

+3

+1 Ottima risposta, ma siamo onesti - è tutto ciò più leggibile/mantenibile di 'if (x Yuck

+0

Direi che è meno leggibile, perché sostituisce un idioma di base con una funzione la cui definizione devi leggere per capire il codice. –

10

No, non cortocircuito e z() sarà sempre valutato. Se vuoi il comportamento di cortocircuito dovresti riscrivere come hai fatto.

+4

Il motivo è perché il runtime non ha modo di sapere quali sono gli effetti di 'Max()' e 'Min()'. ** You ** do, che ti consente di riscrivere in un modo più performante. – Yuck

2

No, non cortocircuito, almeno al livello del compilatore C#. Math.Min o Math.Max sono due ordinarie chiamate al metodo statico e il compilatore non ottimizzerà in questo senso.

L'ordine di valutazione del codice sarà: z(), Math.max, x> ...

Se davvero si vuole fare in modo, controllare il codice IL.

5

Math.Min() e Math.Max() sono metodi come qualsiasi altro. Devono essere valutati per restituire il valore che verrà utilizzato come secondo argomento nel confronto. Se si desidera cortocircuitare, si dovrà scrivere la condizione usando l'operatore || come si è dimostrato.

3

(Niente di particolarmente nuovo da aggiungere, ma ho pensato di condividere i risultati di un test mi sono imbattuto su di esso.)

Math.max() potrebbe facilmente essere inline dal just-in-time compilatore del CLR e da lì ero curioso di sapere se potesse ottimizzare ulteriormente il codice in modo tale da essere cortocircuitato.

Così ho montato un microbenchmark che valuta le due espressioni 1.000.000 volte ciascuna. Per z(), ho usato una funzione che calcola Fib (15) usando il metodo ricorsivo. Ecco i risultati di gestione del due:

x < Math.Max(y, z()) : 8097 ms 
x < y || x < z()  :  29 ms 

sto cercando di indovinare il CLR non trasformerà il codice in alcun modo che impedisce chiamate di metodo di eseguire, perché non sa (e non controlla per vedere se) la routine ha effetti collaterali.

Problemi correlati