Domanda

Durante la revisione, a volte mi incontro con questo tipo di ciclo:

i = begin
while ( i != end ) {    
   // ... do stuff
   if ( i == end-1 (the one-but-last element) ) {
      ... do other stuff
   }
   increment i
}

Quindi faccio la domanda:vuoi scrivere questo?

i = begin
mid = ( end - begin ) / 2 // (the middle element)
while ( i != end ) {    
   // ... do stuff
   if ( i > mid ) {
      ... do other stuff
   }
   increment i
}

A mio parere, questo batte l'intenzione di scrivere un ciclo:è un ciclo, perché c'è qualcosa di comune per essere fatto per ogni elemento.Utilizzando questo costrutto, per alcuni elementi si fa qualcosa di diverso.Quindi, concludo, è necessario un separato loop per questi elementi:

i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {    
   // ... do stuff
   increment i
}

while ( i != end ) {
   // ... do stuff
   // ... do other stuff
   increment i
}

Ora ho visto anche una domanda COSÌ su come scrivere la if-clausola in un bel modo...E ho avuto la triste:qualcosa non è giusto qui.

Sono io che sbaglio?Se è così, che cosa è così buono su di ingombrare il corpo del loop in casi particolari, che si è a conoscenza in anticipo, in tempo di codifica?

È stato utile?

Soluzione

@xtofl,

Sono d'accordo con la tua preoccupazione.

Milioni di volte ho riscontrato un problema simile.

Entrambi gli sviluppatori aggiungono una gestione speciale per il primo o l'ultimo elemento.

Nella maggior parte dei casi vale la pena semplicemente passare da startIdx + 1 o a endIdx - 1 o persino dividere un loop lungo in più loop più corti.

In casi molto rari non è possibile dividere il loop.

Secondo me non comuni le cose dovrebbero essere gestite al di fuori del ciclo ogni volta che è possibile.

Altri suggerimenti

Non credo che a questa domanda debba essere data una risposta in base a un principio (ad es. " in un ciclo, tratta tutti gli elementi allo stesso modo "). Invece, puoi esaminare due fattori per valutare se un'implementazione è buona o cattiva:

  1. Efficacia del runtime: il codice compilato funziona velocemente o sarebbe più veloce farlo diversamente?
  2. Manutenibilità del codice - È facile (per un altro sviluppatore) capire cosa sta succedendo qui?

Se è più veloce e il codice è più leggibile facendo tutto in un ciclo, fallo in quel modo. Se è più lento e meno leggibile, fallo in un altro modo.

Se è più veloce e meno leggibile, o più lento ma più leggibile, scopri quale dei fattori è più importante nel tuo caso specifico, quindi decidi come eseguire il loop (o non eseguire il loop).

So di averlo visto quando le persone hanno provato a unire elementi di un array in una stringa separata da virgola:

for(i=0;i<elements.size;i++) {
   if (i>0) {
     string += ','
   }
   string += elements[i]
}

O hai la clausola if presente o devi duplicare nuovamente la stringa + = riga alla fine.

La soluzione ovvia in questo caso è

string = elements.join(',')

Ma il metodo join fa lo stesso loop internamente. E non c'è sempre un metodo per fare ciò che vuoi.

Sono arrivato alla conclusione che quando metto casi speciali in un ciclo for, di solito sono troppo intelligente per il mio bene.

Nell'ultimo frammento che hai pubblicato, stai ripetendo il codice per // .... do stuff.

Ha senso mantenere 2 loop quando si ha un set di operazioni completamente diverso su un set di indici diverso.

i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {    
   // ... do stuff
   increment i
}

while ( i != end ) {
   // ... do other stuff
   increment i
}

In questo caso, vorresti comunque mantenere un singolo loop. Tuttavia, resta il fatto che si salva ancora (fine - inizio) / 2 numero di confronti. Quindi si riduce a se si desidera che il codice appaia pulito o si desideri salvare alcuni cicli della CPU. La chiamata è tua.

Penso che tu abbia completamente inchiodato.La maggior parte delle persone cadono nella trappola tra condizionali in loop, quando potevano farlo fuori:che è semplicemente più veloce.

Per esempio:

if(items == null)
    return null;

StringBuilder result = new StringBuilder();
if(items.Length != 0)
{
    result.Append(items[0]); // Special case outside loop.
    for(int i = 1; i < items.Length; i++) // Note: we start at element one.
    {
        result.Append(";");
        result.Append(items[i]);
    }
}
return result.ToString();

E il caso di mezzo che hai descritto è solo un semplice brutto.Immaginate se il codice che cresce e ha bisogno di essere rielaborato in diversi metodi.

A meno che non si parsing di XML <grin> loop deve essere mantenuto il più semplice e conciso possibile.

Penso che tu abbia ragione sul fatto che il loop sia pensato per trattare tutti gli elementi allo stesso modo. Purtroppo a volte ci sono casi speciali e questi dovrebbero essere affrontati all'interno del costrutto del ciclo tramite istruzioni if.

Se ci sono molti casi speciali, probabilmente dovresti pensare a trovare un modo per affrontare i due diversi insiemi di elementi in costrutti separati.

Preferisco semplicemente escludere l'elemento dal loop e dare un trattamento separato al di fuori del ciclo

Ad esempio: consideriamo il caso di EOF

i = begin
while ( i != end -1 ) {    
   // ... do stuff for element from begn to second last element
   increment i
}

if(given_array(end -1) != ''){
   // do stuff for the EOF element in the array
}

Ovviamente, le cose con un involucro speciale in un loop che possono essere estratte sono sciocche. Ma non avrei duplicato il do_stuff; Lo metterei in una funzione o in una macro in modo da non copiare e incollare il codice.

Un'altra cosa che odio vedere è il for-case pattern :

for (i=0; i<5; i++)
{
  switch(i)
  {
    case 0:
      // something
      break;
    case 1:
      // something else
      break;
    // etc...
  }
}

L'ho visto in codice reale.

Quale si comporta meglio?

Se il numero di elementi è molto elevato, farei sempre un ciclo, soprattutto se eseguirai alcune operazioni su ogni elemento. Il costo della valutazione del condizionale è probabilmente inferiore a quello del doppio ciclo.

Oops, ovviamente non stai eseguendo il looping due volte ... Nel qual caso è preferibile eseguire due loop. Tuttavia, sostengo che la considerazione principale dovrebbe essere la prestazione. Non è necessario sostenere il condizionale nel ciclo (N volte) se è possibile partizionare il lavoro con una semplice manipolazione dei limiti del ciclo (una volta).

Il caso speciale deve essere eseguito al di fuori del ciclo se deve essere eseguito una sola volta.

Tuttavia, potrebbe esserci un indice o alcune altre variabili che sono più facili da mantenere all'interno del ciclo a causa dell'ambito. Potrebbe anche esserci una ragione contestuale per mantenere tutte le operazioni sulla struttura di dati all'interno della struttura di controllo del ciclo, anche se penso che sia un argomento debole da solo.

Si tratta solo di usarlo secondo necessità e convenienza. Non vi è alcuna menzione per trattare gli elementi allo stesso modo e non vi è certamente alcun danno al clubbing delle caratteristiche fornite dal linguaggio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top