In ifs inside for loops, preferisci controllare true, o false e continuare?

0

Ne discuto con un collega di lavoro. Supponiamo di voler sommare i numeri da 0 a 9 saltando 5. Preferisce questo:

int sum = 0;
for(int i = 0; i < 10; ++i)
{
    if(i == 5)
    {
        continue;
    }

    sum += i;
}

Preferisco questo:

int sum = 0;
for(int i = 0; i < 10; ++i)
{
    if(i != 5)
    {
        sum += i;
    }
}

Qualche motivo per preferire uno rispetto all'altro? La complessità ciclomatica è la stessa in entrambi i casi, giusto?

    
posta FerranMG 04.03.2015 - 18:01
fonte

7 risposte

4

Allo stato attuale (una singola condizione) non vedo molti motivi per preferire l'uno sull'altro, ma probabilmente preferirei il secondo leggermente più semplice.

Quando, tuttavia, il tuo insieme di condizioni è più complesso, alcune varianti del primo possono essere preferibili, spesso con un ampio margine. Prendi in considerazione qualcosa come:

for (...) {
    if (i != 5 && (i%2!=0) && i>99 && i<1000 && (i%17 !=3))
        sum += i;     
}

vs

for (...) {
    if (i==5)
        continue;
    if (i%2 == 0)
        continue;
    if (i<100 || i > 999)
        continue;
    if (i % 17 == 3)
        continue;
    process(i);
}

In questo caso, quest'ultimo sembra (per me) più comprensibile e mantenibile. Ci consente di raggruppare gli elementi logicamente connessi (l'intervallo, 100 ... 1000) insieme, ma di tenere separati quelli che non sono così strettamente correlati in modo che ciascuno possa essere analizzato da solo più facilmente. Inoltre si adatta perfettamente a un pattern che uso in alcune funzioni che fondamentalmente si risolve in qualcosa di simile:

function(x, y, z) { 
    if (!preconditions(x, y, z))
        // return an error or (preferably) throw an exception

    // now do processing on x, y, z
}

Anche se nel tuo caso si tratta di un corpo di loop invece di una funzione di per sé , ti ritroverai comunque con lo stesso schema di base: assicurati che sia soddisfatta una serie di precondizioni e poi se (e solo se) it s, elabora i dati.

    
risposta data 04.03.2015 - 18:41
fonte
5

Personalmente andrei con il primo esempio, dato il tuo compito. Il primo esempio corrisponde più strettamente alla tua affermazione di problema originale. Il secondo richiede un po 'più di analisi mentale per determinare se soddisfa l'affermazione del problema.

Guarda come hai detto cosa sta succedendo: "Somma i numeri da 0 a 9 saltando 5". Questo è esattamente ciò che fa la prima affermazione. Il secondo non si legge nello stesso modo.

In definitiva, ciò che è giusto dipenderà dal contesto del problema più grande che risolverete. Meno pensi di dover fare per verificare che stia facendo quello che dovrebbe, meglio è.

    
risposta data 04.03.2015 - 18:07
fonte
2

Il mio suggerimento sarebbe quello di estrarre tale logica in un metodo in modo che se si aggiungono più condizioni o addirittura si modificino le condizioni è più semplice mantenere in un posto + è più verificabile.

Il motivo per cui sto suggerendo è che sto assumendo che questo non sia il vero problema ma è una versione più semplificata del tuo problema che hai ristretto per dimostrarlo qui.

Se, comunque, le mie supposizioni sono sbagliate, sarebbe comunque una buona idea estrarlo perché lo rende molto più leggibile e comprensibile (soprattutto a causa del nome del metodo e dei nomi delle variabili) - in modo tale che quando tornare indietro dopo un po 'è ancora facile da capire e sapere cosa intendevi quando lo hai scritto la prima volta.

    private void sumItUp()
    {
        int sum = 0;

        for (int numberToAddToSum = 0, i = 0; i < 10; numberToAddToSum = SkipIfNextOneIsFive(i), i++)
        {
            sum += numberToAddToSum;
        }
    }

    private int SkipIfNextOneIsFive(int i) { return i + 1 == 5 ? 0 : ++i; }
    
risposta data 04.03.2015 - 19:50
fonte
1

Entrambi i frammenti di codice stanno facendo troppo lavoro. Puoi sostituirli con int sum = 40; (infatti, se hai dato quei frammenti di codice a un compilatore, questo è fondamentalmente ciò che farebbe il compilatore). C'è un trucco famoso per riassumere i primi n interi che ho usato per farlo nella mia testa, ma se non conoscevi il trucco e non avevi a portata di mano un calcolatore, potresti lavorare con carta e penna in circa la stessa quantità di tempo necessario per digitare quel codice. Quindi il problema, come affermato, non è qualcosa per cui dovresti scrivere un programma.

Naturalmente, non stai chiedendo solo un esempio di questo problema. Che problema stai cercando di risolvere? La domanda va oltre un po ', quindi suppongo che tu sia (a) andando a cercare qualcosa che riassume tutti gli interi consecutivi su un intervallo tranne uno specificato, o (b) che stai andando a sommare tutti gli interi consecutivi in un intervallo, ad eccezione di un intervallo nel mezzo.

Se vuoi (a), puoi semplicemente sottrarre il numero specificato dall'intervallo. Se si desidera (b), è possibile suddividere la vasta gamma in una metà inferiore e una metà superiore e gestirne separatamente ciascuna. Quindi, in entrambi i casi, vogliamo qualcosa che possa riassumere un intervallo di numeri interi.

Se conosci la famosa formula per sommare i primi n interi (n * (n + 1) / 2), e sai come funziona, non è difficile trovare una formula per un intervallo ( (alto-basso + 1) * (top + bottom) / 2). Altrimenti, c'è un modo ovvio e diretto per farlo con i loop.

Se scriviamo una funzione che lo fa (con entrambi i metodi), possiamo riscrivere il problema originale come sum_range(0,9) - 5 o sum_range(0,4) + sum_range(6,9) . Si scopre che non avevamo bisogno di ifs o continua, e, a seconda di come l'abbiamo scritto, forse no loop for.

Se la tua domanda riguarda veramente quale parola chiave usare quando è annidata in un costrutto usando una parola chiave specifica che è annidata in un altro costrutto usando un'altra parola chiave specifica, allora non penso che la tua domanda abbia davvero una risposta, e che provi a capire come programmare osservando quali parole chiave annidano all'interno delle quali altre parole chiave non saranno mai utili.

(Per chi fosse interessato, la formula funziona così: diciamo che stiamo cercando di sommare tutti gli interi da 1 a 100. 1 + 100 = 101, 2 + 99 = 101, 3 + 98 = 101, e così via on, quindi potremmo semplicemente moltiplicare 101 per il numero di coppie, e il numero di coppie è il numero di numeri aggiunti / 2. In questo caso, top = 100, bottom = 1, top + bottom = 101, e il numero di coppie è (top-bottom + 1) / 2 = n / 2 = 50.)

    
risposta data 04.03.2015 - 21:37
fonte
0

Tecnicamente avrà un'altra riga di esecuzione per l'istruzione continue . Altrimenti, il codice è identico. È O (n) in entrambi i casi.

    
risposta data 04.03.2015 - 18:04
fonte
0

Any reason to prefer one over the other?

In alcuni posti e in alcune lingue, l'uso di continue e break è disapprovato come gotos glorificato. Rendono più difficile leggere il codice dal momento che devi fermarti e quindi trovare la fine del ciclo in cui ti trovi piuttosto che notare la condizione e leggerla.

La seconda versione funzionerà praticamente in qualsiasi lingua. continue non è sempre disponibile.

    
risposta data 04.03.2015 - 18:07
fonte
0

La tendenza naturale di A for loops è di "continuare" e, come altri hanno sottolineato, è una linea di codice in più.

In 15 anni di scrittura del codice che ho usato MAYBE continuano (in qualsiasi lingua) 3 volte; e niente di tutto ciò è stato recente. Semplicemente non è un costrutto necessario come ti rendi presto conto.

    
risposta data 04.03.2015 - 18:40
fonte

Leggi altre domande sui tag