2015-11-20 17 views
7

Bei N Elementen nur das erste (0) und letzte (N-1) Element verarbeiten.Schleife nur über erstes und letztes Element

Aber, wenn N = 1, nur das einzelne Element einmal verarbeiten.

Die Verwendung einer Schleife, die je nach Bedarf ein- oder zweimal ausgeführt wird, verhindert, dass der Schleifenkörper dupliziert wird. Wenn es eine lesbare Möglichkeit gibt, dies zu tun, hat dies einen Vorteil für die Quellcodegröße. Es kann auch Vorteile für die Maschinencode-Größe haben, wenn der Schleifenkörper groß ist und der Compiler nicht zu einer Duplizierung führt.


Ich habe versucht, durch N-1 Inkrementieren aber es wird nicht funktionieren, wenn N=1 (Schleifen für immer). Gibt es Tricks (Reverse Loop f.i), die das beheben?

for (i = 0 ; i < N ; i += (N - 1)) 

Edit:

Mein ursprüngliches Problem betrifft drei verschachtelte Schleifen in x, y, z-Richtung, weshalb ich konnte nicht nur Prozess Elem [0]) und Elem [N-1]. Jetzt habe ich die folgende

#define forEachLglBound(i_,j_,k_)         \ 
     for(Int i_ = 0;i_ < NPX;i_+=((NPX>1) ? (NPX-1) : 1))  \ 
      for(Int j_ = 0;j_ < NPY;j_+=((NPY>1) ? (NPY-1) : 1)) \ 
       for(Int k_ = 0;k_ < NPZ;k_+=((NPZ>1) ? (NPZ-1) : 1)) 

+0

Warum brauchen Sie eine Schleife für eine Konstante zwei Elemente? – kaylum

+0

Warum benötigen Sie eine for-Schleife, wenn Sie nur auf das erste und letzte Element zugreifen möchten? –

+6

Warum? Schreiben Sie einfach eine Funktion, um die Arbeit zu erledigen, und rufen Sie sie mit 'array [0]' und 'array [N-1]' auf. Die Absicht wird viel klarer sein. – clcto

Antwort

6

Wie über die folgende Zeile. Sehr nah (textuell!) Zur ursprünglichen Lösung.

for (i = 0 ; i < N ; i += (N > 1) ? N-1 : 1) 
+0

Der Operator * ternary * hat viele einzigartige Anwendungen. Auf den ersten Blick mag es wie ein ziemlich merkwürdiges Bit an Logik aussehen, aber seine inline bedingte Entscheidungsfindung erweitert das, was sonst auf viele nützliche Arten nicht möglich wäre. –

+0

Dies kompiliert zu [ziemlich guten Code] (http://goo.gl/OxdrQW), ist aber nicht sehr leicht zu lesen, IMO. Ich denke, meine Antwort ist lesbarer. –

3
for (i = 0, repeat = 0; repeat < 2 && repeat < N; repeat++, i = N-1) 

oder

repeat = (N<2) ? N : 2; 
for (i = 0; repeat > 0; repeat--, i = N-1) 
+0

Danke. Ich denke, das ist vielleicht der einzige Weg. – user1382302

-1

Wie wäre es damit, wenn auch nicht eine for loop vielleicht verwenden haben, aber es ist ein einfacher Weg, und leicht zu verstehen.

for (int first = 0, last = N - 1;;) { 
    // the stuff use first 
    if (first != last) { 
    // the stuff use last 
    } 
    break; 
} 
+0

Dies verhindert nicht, dass 'stuff' zweimal auf dem gleichen Element ausgeführt wird, wenn' N = 1' ist. Das ist der einzige Grund, warum diese Frage nicht trivial ist. –

+0

Die Sachen benutzen die erste und letzte bedeutet nicht, zuerst zweimal zu verwenden! @PeterCordes – luoluo

+1

Die 'für {...; Unterbrechung; } 'Struktur tut genau nichts. Immer noch ein Downvote, denn wenn du ihm sagen willst, dass er einfach ein 'if' verwenden und das' stuff' wiederholen soll, sag das. –

3

Wenn Sie den Schleifenkörper aus in eine Funktion oder Makro-Faktor können, der wohl lesbare Weise ist:

process(arr[0]); 
if (N-1 != 0) 
    process(arr[N-1]); 

Wenn das keine Option ist, oder Sie denken, eine davon Schleifenstrukturen sind ausreichend lesbar:

XOR kann eine Variable zwischen 0 und dem Wert, mit dem Sie XOR verwenden, umschalten.

int i=0; 
do { 
    process(arr[i]); 
} while(i ^= (N-1)); // xor to toggle, but stays zero if N-1 is zero 

Diese kompiliert besser asm als alle anderen Optionen, as you can see on godbolt. Ich fügte die Ideen aus den anderen Antworten hinzu, einschließlich der einfachen if (die sehr gut ist, wenn die Operation billig ist).

Die XOR-Version funktioniert in einer dreifach verschachtelten Schleife sehr gut (siehe die while_xor_triple-Funktion am unteren Ende der godbolt-Verbindung).


Ohne xor wollen, gibt man diese Schleife mäßig lesbar ist:

int i=0; 
do { 
    process(arr[i]); 
} while((i!=N-1) && (i=N-1)); 

wenn i nicht bereits N-1 ist, um es zu, daß gesetzt und die Schleife wiederholen. Dies sollte es dem Compiler leicht machen, den Test effizient durchzuführen, ohne andere Ausdrücke als N-1 berechnen zu müssen. gcc testet und verzweigt beide Operanden der && jedoch separat, allerdings mit weniger Overhead als einige der anderen Antworten.


Die zweite while-Schleife, so zu etwas zusammenstellen könnte (aber leider nicht der Fall, weil gcc nicht die Tricks von meinen ersten beiden insns tut):

xor edx, edx 
.repeat: 
    mov ecx, edx 
     ... loop body using i (ecx) 
    mov edx, [N] 
    dec edx  ; edx = N-1 
    cmp ecx, edx 
    jne .repeat 

Ich dachte an die XOR-Idee beim Nachdenken darüber, wie man es in asm macht. IDK, wenn ein Smart-Compiler diese Ausgabe von der while(i!=N-1 && i=N-1) Version machen könnten, aber es ist auf jeden Fall besser:

xor ecx, ecx 
.repeat: 
     ... loop body using i (ecx) 
    mov edx, [N] 
    dec edx  ; edx = N-1 
    xor ecx, edx ; i ^= N-1 
    jnz .repeat  ; jump if the last result wasn't zero 

Wenn Sie N-1 in einem Register oder Speicher gespeichert haben, die mov/dec es on the fly weggehen zu berechnen.

Verwandte Themen