2013-08-19 7 views
5

Questo è il codice ipotetica, supponendo che ho il seguente:Come ottimizzare un'istruzione if else if in cui ifs precedenti non vengono utilizzati in loop?

Diciamo che ho un array e ha un sacco di dati, numeri interi in questa domanda di esempio, ma può qualsiasi tipo di dati che è già ordinato in qualche modo a riguarda le dichiarazioni if.

$a = array(0,0,0,1,1,1,1,1,1,2,2,2,2,3,3,...,9,9,9); 

Diciamo che ho un ciclo for con numerose istruzioni if ​​if if, e quelli possono avere qualsiasi criterio per fare qualcosa.

for($i=0; i<count($a); i++) { 
    // these if statements can be anything and may or may not be related with $a 
    if($a[$i] == 0 && $i < 10) { 
     // do something 
    } 
    else if($a[$i] == 1 && $i < 20) { 
     // do something 
    } 
    else if($a[$i] == 2) { 
     // do something 
    } 
    else if($a[$i] == 3) { 
     // do something 
    } 

    // and so on 
} 

Ora la domanda, dopo le prime iterazioni di istruzioni if, non viene mai utilizzata. Una volta che il ciclo for inizia a utilizzare l'istruzione if successiva, le istruzioni if ​​precedenti non devono essere nuovamente valutate. Può usare la prima istruzione se n quantità di volte e così via e così via.

C'è un modo per ottimizzarlo in modo che non debba passare attraverso tutte le precedenti istruzioni if ​​else if mentre esegue il looping dei dati? La mente, i dati possono essere qualsiasi cosa, e le dichiarazioni if ​​possono essere una varietà di condizioni.

C'è un cambio di paradigma, che non vedo, che è richiesto su come questo dovrebbe essere codificato per fornire prestazioni ottimali?

+2

Forse ... mantenere una variabile 'currentTier', controllare se ha bisogno di essere aggiornata (e farlo) all'inizio del ciclo, e quindi 'passare' su quello? – Michelle

+0

@Michelle: lo stesso problema, in pratica. Una volta che il livello è sufficientemente alto, i primi casi sono sempre inutilizzati. Inoltre, stiamo parlando di intervalli anche qui. –

+0

@ Madara> Penso che sia possibile assegnare un indice a ciascuno di if() e quindi utilizzare un interruttore() per guardare l'indice giusto. Se non si interrompe ogni caso, è possibile passare da if a if senza dover valutare i primi – koopajah

risposta

0

Suddividi le istruzioni for in multiple per le istruzioni. Per il vostro codice di esempio:

for($i=0; i<10; i++) { 
    if($a[$i] == 0) { 
     //do something 
    } 
} 

for($i=0; i<20; i++) { 
    if($a[$i] == 1) { 
     //do something 
    } 
} 

for($i=0; $i<count($a); $i++) { 
    if($a[$i] == 2) { 
     // do something 
    } 
    else if($a[$i] == 3) { 
     // do something 
    } 
} 

//etc... 
+0

Questo potrebbe effettivamente essere il miglior rendimento, ma non del tutto flessibile. –

+0

@Madara Ho provato una soluzione con un ciclo while attorno a una singola istruzione, ma poi è costretta a tornare in uno scenario di dichiarazione multipla. Questo metodo isola la variabile incrementale in modo che non debba essere testata affatto. –

+0

Ciò non riconosce che i dati possono essere qualsiasi cosa e le istruzioni if ​​possono essere una varietà di condizioni. – rotaercz

0

PHP usa qualcosa chiamato "la valutazione di corto circuito", come molte altre lingue moderne fanno. Ciò significa che una volta che l'espressione booleana è stata determinata come vera o falsa, i pezzi rimanenti dell'espressione non saranno valutati.

Quindi, è possibile introdurre nuovi valori booleani (forse un array di essi) che traccia se un pezzo di codice è già stato eseguito, e se lo è stato, impostarlo su falso. Quindi usa questo booleano come prima condizione nell'espressione "if". PHP riconoscerà che il valore di questo è impostato su false e ignora il resto della clausola. Questo è un percorso piuttosto semplice e manterrebbe il tuo codice strutturato per lo più come è ora.

1

È possibile utilizzare call_user_func_array. Dovresti creare una classe che memorizza i metodi da chiamare per eseguire le istruzioni. Si consideri una classe come questa:

class MyStatements { 
    public function If0($a, $i) { 
     if($a[$i] == 0 && $i < 10) { 
      // do something 
     } 
    } 

    public function If1($a, $i) { 
     if($a[$i] == 1 && $i < 20) { 
      // do something 
     } 
    } 
} 

si potrebbe poi fare qualcosa di simile:

$stmts = new MyStatements(); 
for($i = 0; i < count($a); i++) { 
    call_user_func_array(array($stmts, 'If' . strval($i)), array($a, $i)); 
} 
+0

Questo sembra bello, ma questo non sarebbe chiaramente peggiore nelle prestazioni? – koopajah

+0

@koopajah, non proprio. Sarebbe ancora un'operazione O (n) per l'iterazione. –

+1

Sì, ma non ci sono alcuni costi impliciti da call_user_func_array() qui? – koopajah

0

Penso che si sta girare le ruote.

Se si dispone di molti dati, è probabile che la lentezza dei dati provenga dall'origine dati e non dai calcoli sul lato server.

Se si fa qualcosa, è necessario suddividere i dati in blocchi ed eseguire porzioni alla volta. E avresti bisogno di farlo solo se stai notando tempi di caricamento lenti o un cattivo carico di lavoro sul tuo server.

Connessioni asincrone consentono di eseguire questa operazione con facilità, utilizzando ajax è possibile connettersi al server, estrarre una porzione limitata di dati, elaborarli, quindi visualizzarli nel browser client, eseguire il blocco successivo. Ogni volta che usi un sito Web che richiede grandi quantità di dati (ad esempio: facebook), lo fa in questo modo.

Ma ancora, non pensarci troppo. Non hai davvero bisogno di rendere la tua procedura più complicata. Se vuoi davvero una stella d'oro, puoi creare una classe orientata agli oggetti che elabori tutto questo per te, ma non ci penserò.

+0

(Aggiungerò anche, se stai tracciando enormi quantità di dati, dovresti anche considerare di memorizzare i dati in stringhe json in ordine alfabetico, che toglie la maggior parte del carico dal tuo server e se sei veramente cleaver puoi spingere i dati al client -side tramite localStorage (ma non impazzire con questo) –

+0

Penso che questa sia la risposta più logica Se hai una data che deve essere elaborata con la logica, non c'è modo di aggirarla, e ci sarà un costo. – John

0

Se si utilizza PHP 5.3+, è possibile utilizzare anonymous functions.

$a = array(0,0,0,1,1,1,1,1,1,2,2,2,2,3,3,9,9,9); 

$dispatch = array(
    0=>function() { echo "0"; }, 
    1=>function() { echo "1"; }, 
    2=>function() { echo "2"; }, 
    3=>function() { echo "3"; }, 
    9=>function() { echo "9"; } 
); 

foreach ($a as $i) 
{ 
    $dispatch[$i](); 
} 

Prima di PHP 5.3 si dovrà utilizzare una mappa di funzionare nomi, ma il fondo lavora a PHP 5.3+ pure.

$a = array(0,0,0,1,1,1,1,1,1,2,2,2,2,3,3,9,9,9); 

function foo0() { echo "0"; } 
function foo1() { echo "1"; } 
function foo2() { echo "2"; } 
function foo3() { echo "3"; } 
function foo9() { echo "9"; } 

$dispatch = array(
    0=>"foo0", 
    1=>"foo1", 
    2=>"foo2", 
    3=>"foo3", 
    9=>"foo9" 
); 

foreach ($a as $i) 
{ 
    $dispatch[$i](); 
} 

Il codice sopra riportato è più veloce, ma non completamente efficiente. Per migliorare le prestazioni dovresti eliminare la ricerca chiave nell'array $ dispatch e andare avanti ogni volta che il valore di $ a [#] cambia. Questo presuppone che l'array $ dispatch corrisponda all'array di input. Otterrai solo un miglioramento delle prestazioni se l'array $ dispatch fosse molto grande.

$a = array(0,0,0,1,1,1,1,1,1,2,2,2,2,3,3,9,9,9); 

function foo0() { echo "0"; } 
function foo1() { echo "1"; } 
function foo2() { echo "2"; } 
function foo3() { echo "3"; } 
function foo9() { echo "9"; } 

$dispatch = array(
    0=>"foo0", 
    1=>"foo1", 
    2=>"foo2", 
    3=>"foo3", 
    9=>"foo9" 
); 

reset($dispatch); 
$foo = (string)current($dispatch); 
$last = 0; 
foreach ($a as $i) 
{ 
    $foo(); 
    if($i != $last) 
    { 
     $foo = (string)next($dispatch); 
     $last = $i; 
    } 
} 

Questo dovrebbe essere il più efficiente possibile.

0

Non sono sicuro di quanto sia diverso da quello della Soluzione, ma avevo pensato di buttarlo lì.

function func1() { 
    echo "hi\n"; 
} 
function func2() { 
    echo "bye\n"; 
} 
$functionList = array (
    0 => "func1", 
    1 => "func2" 
); 
$a = array(0,0,0,1,1,1,1,1,1,2,2,2,2,3,3,9,9,9); 
$len = count($a); 
for($i = 0; $i < $len; $i++) { 
    if (isset($functionList[$i])) { 
     call_user_func($functionList[$i]); 
    } 
} 

ho impostato le chiavi di $functionList esplicitamente, dal momento OP dice che non saranno sempre numerico. Forse i primi 2-3 compiti potrebbero essere inclusi in una classe.

0

Questa soluzione verbose impedirà l'esecuzione di qualsiasi condizione di if dopo che è stata valutata su false e non verrà iterata sullo stesso valore $i più di una volta, tranne quando transiterà al ciclo successivo.

for($i=0; i<count($a); i++) { 
    if($firstCondition) { 
     //do something 
    } else { 
     break; 
    } 
} 

for($i; i<count($a); i++) { 
    if($secondCondition) { 
     //do something 
    } else { 
     break; 
    } 
}