Wenn der Compiler auf ein foreach
-Konstrukt trifft...
foreach (element; range)
{
// Loop body...
}
wird es intern etwa wie folgt umgeschrieben:
for (auto __rangeCopy = range;
!__rangeCopy.empty;
__rangeCopy.popFront())
{
auto element = __rangeCopy.front;
// Loop body...
}
Falls das Range-Objekt ein Referenztyp ist (z.B. class
), wird es
verbraucht und ist daher für weitere Iterationen nicht mehr verfügbar
(es sei denn, der Schleifenrumpf bricht vor der letzten Iteration ab).
Falls das Range-Objekt ein Werttyp ist, wird eine Kopie der Range
erzeugt und - abhängig von der Definition - die ursprüngliche
Range verbraucht.
Die meisten Ranges der Standard-Bibliothek sind Strukturen (struct
),
sodass eine Iteration normalerweise nicht zerstörend wirkt - allerdings
nicht garantiert. Sollte diese Garantie wichtig sein, sind forward
-Ranges erforderlich.
Jedes Objekt mit folgendem Interface wird Range genannt und kann somit iteriert werden:
struct Range
{
T front() const @property;
bool empty() const @property;
void popFront();
}
Beachte: Obwohl empty
und front
gewöhnlich als const
-Funktionen
definiert werden (was impliziert, dass ein Aufruf die Range nicht
modifiziert), ist dies nicht erforderlich.
Die Funktionen in std.range
und std.algorithm
stellen Bausteine
zur Verfügung, die dieses Interface nutzen. Ranges erlauben die
einfache Erstellung komplexer iterierender Algorithmen.
Auch erlauben Ranges die Erstellung von Lazy-Objekten, die
ihre Berechnungen nur ausführen, wenn dies wirklich nötig ist,
z.B. wenn während einer Iteration auf das nächste Range-Element
zugegriffen wird.
Vervollständige den Quellcode, um eine FibonacciRange
erzeugen,
die Zahlen der Fibonacci-Folge
generiert. Derassert
-Befehl am Ende stellt die Korrektheit deiner
Implementation sicher!