Die wohl elementarste Schleife in VB (sowohl classic als auch .Net) dürfte wohl die "For Next" Schleife - auch "Zählschleife" genannt - sein.
In ihrer einfachsten Form sieht sie so aus:
Wir zählen von 1 bis 10 und geben den Wert unserer "Zählvariablen" aus. Sinnlos aber zur Einführung mag das jetzt reichen
Meistens "machen" wir aber was sinnvolles mit unserem "i". ZB sprechen wir die einzelnen Elemente eines "Arrays" an:
Neben Arrays gibt es natürlich noch andere Sachen, die wir über ihren "Index" ansprechen können. Im Prinzip alles, was das "Interface" IEnumerable unterstützt. "Enumerable" bedeutet ja letztlich nichts anderes als "nummerierbar". Sogar Arrays (die ja ganz simple Aufzählungen sind) unterstützen dieses Interface:
Wiederum reichlich sinnlos, denn warum sollten wir uns das Interface "ziehen", wenn wir die Elemente auch direkt ansprechen können?
Das Interface nutzen wir (meist unbemerkt) dann, wenn wir stattdessen das "For Each" Konstrukt verwenden:
Wir haben jetzt also keine "Zählvariable" mehr, sondern sagen einfach "nimm jedes Element und ...".
Was ist jetzt aber "besser". Nun ... das hängt davon ab
"For Each" erzeugt einen gewissen "Overhead" - es wird also zusätzlicher Code (im hintergrund) erzeugt, um die Aufzählung zu machen. Das bedeutet, dass "For next" immer schneller ist? Nein! Wenn ich eine "statische" Aufzählung habe, dann ist es mit dem Index oft (aber nicht immer, das hängt vom Typ der Aufzählung ab!) schneller - wobei das häufig nur dann zum Tragen kommt, wenn ich SEHR viele Elemente habe und IN der Schleife nur etwas sehr einfaches/schnelles mache. Dh das Schleifenkonstrukt selbst hat einen großen Anteil am Aufwand.
Aber wenn man zb sowas hat:
Was macht das? "Iterator" ist eine neue Funktion (aus dem CTP Async), die es erlaubt - ganz einfach! - in VB.Net EIGENE Aufzählungsfunktionen zu schreiben, wobei natürlich im Net Framework schon diverse Methoden drin sind, die genau so (!) funktionieren.
Ok. Unsere Funktion nimmt einen "Pfad" (zu einer Textdatei) und liefert jeweils "eine Zeile" zurück.
Man verwendet das ganze so:
("data.txt" ist eine Textdatei, die drei Zeilen enthält in denen jeweils 1,2 und 3 drinstehen)
Läßt man das ganze laufen, bekommt man folgenden output:
Ziemlich trivial - wie man denken könnte
Aber was passiert, wenn ich nicht "For Each", sondern die "normale" For Next Schleife nehme?
Unser output sieht dann so aus:
Bawoom! Was ist denn JETZT passiert??
Wir verwenden die .Count Eigenschaft um eine obere Grenze für unsere Zählvariable zu ermitteln. Also muss natürlich erstmal JEDE Zeile gelesen werden, damit man eine ANZAHL hat. Das erklärt die ersten drei inneren Aufrufe.
Als nächstes sprechen wir jedes einzelne Element an. Um dann das Element Nummer "X" zu finden muss unsere Funktion ALLE Elemente vorher einlesen (und verwerfen, weil sie gar nicht benötigt werden). Und wie man sieht, geht das mit steigender Elementzahl ziemlich rasch nach oben.
ACHTUNG Mathematik:
Im ersten Fall für X Elemente wird unsere "inside" Operation genau X Mal ausgeführt. Immer
Im zweiten Fall:
X (für die Bestimmung der Anzahl) PLUS (1 + X) * X/2 (Gauss!)
Wir haben also einen "Overhead" von (X + X^2)/2. Dh. der Aufwand wächst QUADRATISCH! Man stelle sich mal eine Textdatei mit 1 Mio Zeilen vor ...
Was lernen wir also:
Wenn wir uns 120% sicher sind, nehmen wir "For i ... next". Wenn wir uns unsicher sind (was da im hintergrund nun WIRKLICH abläuft), nehmen wir "For each"
Man sehe sich zb mal "System.IO.File.ReadLines" vs "System.IO.File.ReadAllLines" an! Die erste Version liefert nämlich genau unser IENumerable(Of String) zurück und die Verwendung von "For index ... next" wäre da superkontraproduktiv.
IMHO natürlich und Kommentare willkommen
In ihrer einfachsten Form sieht sie so aus:
Wir zählen von 1 bis 10 und geben den Wert unserer "Zählvariablen" aus. Sinnlos aber zur Einführung mag das jetzt reichen
Meistens "machen" wir aber was sinnvolles mit unserem "i". ZB sprechen wir die einzelnen Elemente eines "Arrays" an:
Neben Arrays gibt es natürlich noch andere Sachen, die wir über ihren "Index" ansprechen können. Im Prinzip alles, was das "Interface" IEnumerable unterstützt. "Enumerable" bedeutet ja letztlich nichts anderes als "nummerierbar". Sogar Arrays (die ja ganz simple Aufzählungen sind) unterstützen dieses Interface:
Wiederum reichlich sinnlos, denn warum sollten wir uns das Interface "ziehen", wenn wir die Elemente auch direkt ansprechen können?
Das Interface nutzen wir (meist unbemerkt) dann, wenn wir stattdessen das "For Each" Konstrukt verwenden:
Wir haben jetzt also keine "Zählvariable" mehr, sondern sagen einfach "nimm jedes Element und ...".
Was ist jetzt aber "besser". Nun ... das hängt davon ab
"For Each" erzeugt einen gewissen "Overhead" - es wird also zusätzlicher Code (im hintergrund) erzeugt, um die Aufzählung zu machen. Das bedeutet, dass "For next" immer schneller ist? Nein! Wenn ich eine "statische" Aufzählung habe, dann ist es mit dem Index oft (aber nicht immer, das hängt vom Typ der Aufzählung ab!) schneller - wobei das häufig nur dann zum Tragen kommt, wenn ich SEHR viele Elemente habe und IN der Schleife nur etwas sehr einfaches/schnelles mache. Dh das Schleifenkonstrukt selbst hat einen großen Anteil am Aufwand.
Aber wenn man zb sowas hat:
Was macht das? "Iterator" ist eine neue Funktion (aus dem CTP Async), die es erlaubt - ganz einfach! - in VB.Net EIGENE Aufzählungsfunktionen zu schreiben, wobei natürlich im Net Framework schon diverse Methoden drin sind, die genau so (!) funktionieren.
Ok. Unsere Funktion nimmt einen "Pfad" (zu einer Textdatei) und liefert jeweils "eine Zeile" zurück.
Man verwendet das ganze so:
("data.txt" ist eine Textdatei, die drei Zeilen enthält in denen jeweils 1,2 und 3 drinstehen)
Läßt man das ganze laufen, bekommt man folgenden output:
Ziemlich trivial - wie man denken könnte
Aber was passiert, wenn ich nicht "For Each", sondern die "normale" For Next Schleife nehme?
Unser output sieht dann so aus:
Bawoom! Was ist denn JETZT passiert??
Wir verwenden die .Count Eigenschaft um eine obere Grenze für unsere Zählvariable zu ermitteln. Also muss natürlich erstmal JEDE Zeile gelesen werden, damit man eine ANZAHL hat. Das erklärt die ersten drei inneren Aufrufe.
Als nächstes sprechen wir jedes einzelne Element an. Um dann das Element Nummer "X" zu finden muss unsere Funktion ALLE Elemente vorher einlesen (und verwerfen, weil sie gar nicht benötigt werden). Und wie man sieht, geht das mit steigender Elementzahl ziemlich rasch nach oben.
ACHTUNG Mathematik:
Im ersten Fall für X Elemente wird unsere "inside" Operation genau X Mal ausgeführt. Immer
Im zweiten Fall:
X (für die Bestimmung der Anzahl) PLUS (1 + X) * X/2 (Gauss!)
Wir haben also einen "Overhead" von (X + X^2)/2. Dh. der Aufwand wächst QUADRATISCH! Man stelle sich mal eine Textdatei mit 1 Mio Zeilen vor ...
Was lernen wir also:
Wenn wir uns 120% sicher sind, nehmen wir "For i ... next". Wenn wir uns unsicher sind (was da im hintergrund nun WIRKLICH abläuft), nehmen wir "For each"
Man sehe sich zb mal "System.IO.File.ReadLines" vs "System.IO.File.ReadAllLines" an! Die erste Version liefert nämlich genau unser IENumerable(Of String) zurück und die Verwendung von "For index ... next" wäre da superkontraproduktiv.
IMHO natürlich und Kommentare willkommen