diff --git a/.github/workflows/d.yml b/.github/workflows/d.yml index 4521659..34a82bb 100644 --- a/.github/workflows/d.yml +++ b/.github/workflows/d.yml @@ -21,9 +21,15 @@ jobs: - name: 'Run sanitycheck' run: | echo "Check for trailing whitespace" - grep -nr --include \*.md '\s$' . ; test $? -eq 1 + if [ grep -nr --include \*.md '\s$' . ]; + echo "Found trailing whitespace, please remove" + exit 1 + fi echo "Check for tabs" - grep -nrP --include \*.md '\t' . ; test $? -eq 1 + if [ grep -nrP --include \*.md '\t' . ]; + echo "Found tabs, please replace with spaces" + exit 1 + fi git clone https://github.com/dlang-tour/core ../tour mkdir -p ../tour/public/content/lang mv * ../tour/public/content/lang diff --git a/basics/alias-strings.md b/basics/alias-strings.md index d75f730..0a8d7c1 100644 --- a/basics/alias-strings.md +++ b/basics/alias-strings.md @@ -1,22 +1,22 @@ # Alias & Strings -Nun, da uns Arrays bekannt sind und wir mit den Basis-Typen +Nun, da uns Arrays bekannt sind und wir mit den Basis-Typen und dem Schlüsselwort`immutable` beschäftigt haben, wird es Zeit zwei neue Konstrukte in einer Zeile einzuführen: alias string = immutable(char)[]; - -Der Begriff `string` wird durch das Schlüsselwort `alias` definiert, + +Der Begriff `string` wird durch das Schlüsselwort `alias` definiert, und zwar als Slice bestehend aus `immutable(char)`s. -Das bedeutet, sobald ein `string` konstruiert wurde, wird sich sein +Das bedeutet, sobald ein `string` konstruiert wurde, wird sich sein Inhalt nie mehr ändern. Und damit sind wir bei dem zweiten Konstrukt: Willkommen UTF-8 `string`! -Aufgrund ihrer Unveränderlichkeit (engl.: immutablility) können -`string`s über verschiedene Threads hinweg geteilt werden. Da `string` +Aufgrund ihrer Unveränderlichkeit (engl.: immutablility) können +`string`s über verschiedene Threads hinweg geteilt werden. Da `string` ein Slice ist, können Teile ohne Speicher-Allokationen entnommnen werden. -Die Standard-Funktion `std.algorithm.splitter`](https://dlang.org/phobos/std_algorithm_iteration.html#.splitter) -z.B. teilt einen String anhand von Zeilensprüngen (newline-Zeichen) +Die Standard-Funktion `std.algorithm.splitter`](https://dlang.org/phobos/std_algorithm_iteration.html#.splitter) +z.B. teilt einen String anhand von Zeilensprüngen (newline-Zeichen) ohne jede Speicher-Allokation. Neben dem UTF-8 `string` gibt es zwei weitere Typen: @@ -24,7 +24,7 @@ Neben dem UTF-8 `string` gibt es zwei weitere Typen: alias wstring = immutable(wchar)[]; // UTF-16 alias dstring = immutable(dchar)[]; // UTF-32 -Diese Varianten können durch Nutzung der `to`-Methode aus `std.conv` +Diese Varianten können durch Nutzung der `to`-Methode aus `std.conv` einfach ineinander konvertiert werden: dstring myDstring = to!dstring(myString); @@ -33,10 +33,10 @@ einfach ineinander konvertiert werden: ### Unicode Strings Ein einfacher `string` ist als ein Array aus 8-bit Unicode [code -units](http://unicode.org/glossary/#code_unit) definiert. Alle Array-Operationen -können auf Strings angewandt werden, aber dies wird nur auf der Code-Unit-Ebene +units](http://unicode.org/glossary/#code_unit) definiert. Alle Array-Operationen +können auf Strings angewandt werden, aber dies wird nur auf der Code-Unit-Ebene funktionieren, nicht aber auf Zeichen-Ebene! Gleichzeitig interpretieren die -Algorithmen der Standard-Bibliothek Strings als Sequenzen aus +Algorithmen der Standard-Bibliothek Strings als Sequenzen aus [Code Points](http://unicode.org/glossary/#code_point). Darüber hinaus gibt es die Option der Behandlung der Sequenz als [Grapheme](http://unicode.org/glossary/#grapheme) durch explizite Nutznung von @@ -56,22 +56,22 @@ Diese kleine Beispiel verdeutlicht die unterschiedlichen Interpretationen: Hier ist die tatsächliche Länge des Arrays `s` 3, weil es 3 Code Units enthält: `0x41`, `0x03` and `0x08`. Von diesen definieren zwei einen einzelnen -Code Point (kombinierende diakritische Zeichen) und +Code Point (kombinierende diakritische Zeichen) und [`walkLength`](https://dlang.org/library/std/range/primitives/walk_length.html) (Funktion der Standard-Bibliothek zur Berechnung der Länge beliebiger Ranges) zählt zwei Code Points. Schließlich ermittelt `byGrapheme` in einer aufwändigeren -Berechnung, dass sich diese beiden Code Points zu einem einzigen angezeigten +Berechnung, dass sich diese beiden Code Points zu einem einzigen angezeigten Zeichen zusammensetzen. -Korrekte Unicode-Verarbeitung kann sehr kompliziert sein. Trotzdem dürfen +Korrekte Unicode-Verarbeitung kann sehr kompliziert sein. Trotzdem dürfen D-Entwickler String-Variablen als magische Byte-Arrays betrachten und sich darauf -verlassen, dass die Standard-Bibliothek "das Richtige tut". -Die meiste Unicode-Funktionalität wird in dem Modul +verlassen, dass die Standard-Bibliothek "das Richtige tut". +Die meiste Unicode-Funktionalität wird in dem Modul [`std.uni`](https://dlang.org/library/std/uni.html) bereitgestellt, Grundlegenderes in [`std.utf`](https://dlang.org/library/std/utf.html). ### Mehrzeilige Strings -Für die Erzeugung mehrzeiliger Strings bietet sich die +Für die Erzeugung mehrzeiliger Strings bietet sich die `string str = q{ ... }`-Syntax an: string multiline = q{ This @@ -83,7 +83,7 @@ Für die Erzeugung mehrzeiliger Strings bietet sich die Auch ist es möglich Raw Strings zu benutzen, um die aufwändige Verarbeitung von reservierten Symbolen zu minimieren. Raw Strings können mittels Backticks -(invertierte Hochkommanta) (`` ` ... ` ``) oder den r(aw)-Präfix (`r" ... "`) +(invertierte Hochkommanta) (`` ` ... ` ``) oder den r(aw)-Präfix (`r" ... "`) deklariert werden. string raw = `raw "string"`; // raw "string" @@ -107,7 +107,7 @@ import std.string: format; void main() { // format generiert einen String mittels - // printf-artiger Syntax. D erlaubt + // printf-artiger Syntax. D erlaubt // native UTF-String-Verarbeitung! string str = format("%s %s", "Hellö", "Wörld"); @@ -120,7 +120,7 @@ void main() { ~ " of string: ", str.byGrapheme.walkLength); - // Strings sind einfache Arrays! + // Strings sind einfache Arrays! // Somit funktionieren alle Array- // Operation auch mit Strings! import std.array: replace; diff --git a/basics/associative-arrays.md b/basics/associative-arrays.md index 74c8608..f04a179 100644 --- a/basics/associative-arrays.md +++ b/basics/associative-arrays.md @@ -1,8 +1,8 @@ # Assoziative Arrays -D besitzt integrierte *Assoziative Arrays* (auch als Hashtabelle -oder Hashmap bekannt). Ein Assoziatives Array mit ein Schlüssel -des Typs `string` und einem Wert des Typs `int` wird folgendermaßen +D besitzt integrierte *Assoziative Arrays* (auch als Hashtabelle +oder Hashmap bekannt). Ein Assoziatives Array mit ein Schlüssel +des Typs `string` und einem Wert des Typs `int` wird folgendermaßen erzeugt: int[string] arr; @@ -17,22 +17,22 @@ ist, kann der `in`-Ausdruck genutzt werden: if ("key1" in arr) writeln("Yes"); -Der `in`-Ausdruck gibt einen Zeiger auf den Wert zurück, falls er +Der `in`-Ausdruck gibt einen Zeiger auf den Wert zurück, falls er gefunden wurde, anderenfalls einen `null`-Zeiger. Also können Existenz-Test und Schreiben komfortabel kombiniert werden: if (auto val = "key1" in arr) *val = 20; -Zugriff auf einen nicht existierenden Schlüssel führt zu einem -`RangeError` und dem sofortigen Abbruch der Anwendung. Für +Zugriff auf einen nicht existierenden Schlüssel führt zu einem +`RangeError` und dem sofortigen Abbruch der Anwendung. Für sicheren Zugriff mit einen Standardwert (engl: default value) gibt es `get(key, defaultValue)`. Assoziativery Arrays bieten neben der `.length`-Eigenschaft -herkömmlicher Arrays auch `.remove(val)`, um Einträge über -ihren Schlüssel zu entfernen. -Dem Leser wird als Übung empfohlen, die speziellen `.byKey`- +herkömmlicher Arrays auch `.remove(val)`, um Einträge über +ihren Schlüssel zu entfernen. +Dem Leser wird als Übung empfohlen, die speziellen `.byKey`- und `.byValue`-Ranges zu erforschen. ### Weiterführende Quellen @@ -61,13 +61,13 @@ int[string] wordCount(string text) import std.algorithm.iteration : splitter; import std.string : toLower; - // Durch Wörter indiziert und Anzahl + // Durch Wörter indiziert und Anzahl // zurückgebend int[string] words; foreach(word; splitter(text.toLower(), " ")) { - // Inkrementiere Wortzähler + // Inkrementiere Wortzähler // falls ein Wort gefunden wurde. // Integer-Standartwert ist 0. words[word]++; diff --git a/basics/classes.md b/basics/classes.md index e6b5ef8..2c6ea0d 100644 --- a/basics/classes.md +++ b/basics/classes.md @@ -12,7 +12,7 @@ In D werden Klassen generell mit `new` auf dem Heap instanziiert: auto bar = new Bar; -Klassenobjekte sind immer Referenztypen. Anders als `struct`s, +Klassenobjekte sind immer Referenztypen. Anders als `struct`s, die beim Aufruf kopiert werden (engl: call by value), werden Objekte nur als Referenz weitergereicht (engl.: call by reference). @@ -24,7 +24,7 @@ freigegeben wird, sobald keine Referenzen mehr auf das Objekt existieren. ### Vererbung Wenn eine Memberfunktion einer Basisklasse überschrieben wird, muss das -Schlüsselwort `override` benutzt werden, um dies anzuzeigen. So wird +Schlüsselwort `override` benutzt werden, um dies anzuzeigen. So wird ungewolltes Überschreiben von Funktionen vermieden. class Bar: Foo { @@ -35,9 +35,9 @@ In D können Klassen nur von Klassen erben. ### Final und abstract Memberfunktionen -- Eine Funktion kann als `final` markiert werden, um das Überschreiben +- Eine Funktion kann als `final` markiert werden, um das Überschreiben zu verbieten -- Eine Funktion kann als `abstract` markiert werden, um das Überschreiben +- Eine Funktion kann als `abstract` markiert werden, um das Überschreiben zu erzwingen - Eine ganze Klasse kann als `abstract` deklariert werden, um sicherzustellen, dass sie nicht instanziiert wird @@ -45,10 +45,10 @@ dass sie nicht instanziiert wird ### Prüfen der Identität -Der Inhalt von Klassenobjekten wird mithilfe der Operatoren `==` and `!=` +Der Inhalt von Klassenobjekten wird mithilfe der Operatoren `==` and `!=` verglichen. Daher ist der Vergleich gegen `null` nicht zulässig, da `null` keinen Inhalt besitzt. -Das `is` vergleicht die Identität von Objekten. Um auf Nicht-Identität zu prüfen, +Das `is` vergleicht die Identität von Objekten. Um auf Nicht-Identität zu prüfen, sollte `e1 !is e2` verwendet werden. ```d @@ -75,7 +75,7 @@ ist Identität gleichwertig mit Gleichheit. import std.stdio : writeln; /* -Ausgefallener Typ der für alles genutzt +Ausgefallener Typ der für alles genutzt werden kann... */ class Any { @@ -92,7 +92,7 @@ class Any { return type; } - // Diese Fuktion muss + // Diese Fuktion muss // implementiert werden! abstract string convertToString(); } @@ -111,12 +111,12 @@ class Integer: Any { this.number = number; } - // Dies ist implizit. + // Dies ist implizit. public: override string convertToString() { import std.conv : to; - // Das Schweizer-Taschenmesser + // Das Schweizer-Taschenmesser // der Konvertierung... return to!string(number); } @@ -145,7 +145,7 @@ void main() ]; foreach (any; anys) { - writeln("Typ von any = ", + writeln("Typ von any = ", any.getType()); writeln("Inhalt = ", any.convertToString()); diff --git a/basics/delegates.md b/basics/delegates.md index 7e7b0de..99c0c6f 100644 --- a/basics/delegates.md +++ b/basics/delegates.md @@ -12,7 +12,7 @@ Eine Funktion kann auch Parameter einer Funktion sein: doSomething(add); // use global function `add` here // add must have 2 int parameters -`doer` kann wie jede andere normale Funktion aufgerufen +`doer` kann wie jede andere normale Funktion aufgerufen werden. ### Lokale Funktionen mit Kontext @@ -22,11 +22,11 @@ ein Zeiger auf eine globale Funktion. Sobald auf eine Memberfunktion oder eine lokale Funktion verwiesen wird, muss ein `delegate` verwendet werden. Das ist ein Funktionszeiger mit zusätzlichen Informationen zu seinem Kontext (in anderen Programmiersprachen -auch **Closure** genannt). Ein auf eine Memberfunktion einer +auch **Closure** genannt). Ein auf eine Memberfunktion einer Klasse zeigendes `delegate` enthält z.B. einen Zeiger auf das Klassenobjekt. Ein `delegate`, erzeugt in einer verschachtelten -Funktion, enthält stattdessen einen Link zu seinem umgebenden -Kontext. Allerdings darf der D-Compiler automatisch eine Kopie +Funktion, enthält stattdessen einen Link zu seinem umgebenden +Kontext. Allerdings darf der D-Compiler automatisch eine Kopie des Kontextes auf dem Heap erstellen, falls dies der Speicher- sicherheit dient. Das Delegate enthält dann einen Link zu diesem Heap-Bereich @@ -43,23 +43,23 @@ würde so aussehen: void doSomething(int delegate(int,int) doer); -`delegate`- und `function`-Objekte können nicht gemischt werden. +`delegate`- und `function`-Objekte können nicht gemischt werden. Daher konvertiert die Standardfunktion [`std.functional.toDelegate`](https://dlang.org/phobos/std_functional.html#.toDelegate) eine `function` in ein `delegate`. ### Anonyme Funktionen und Lambdafunktionen -Da Funktionen als Variablen gespeichert und an andere Funktionen +Da Funktionen als Variablen gespeichert und an andere Funktionen übergeben werden können, ist es mühsam, sie zu definieren und ihnen -einen Namen zu geben. Daher erlaubt D namenlose Funktionen und +einen Namen zu geben. Daher erlaubt D namenlose Funktionen und einzeilige _Lambdafunktionen_. auto f = (int lhs, int rhs) { return lhs + rhs; }; // Lambdafunktion, gleich mit obiger Funktion - auto f = (int lhs, int rhs) => lhs + rhs; + auto f = (int lhs, int rhs) => lhs + rhs; Auch besteht die Möglichkeit, Strings als Template-Argumente an funktionale Teile der D-Standardbibliothek zu nutzen. Dies erlaubt @@ -67,7 +67,7 @@ z.B. eine komfortable Art der Definition einer Reduce-Funktion: [1, 2, 3].reduce!`a + b`; // 6 -String-Funktionen sind nur möglich für _ein oder zwei_ Argumente, +String-Funktionen sind nur möglich für _ein oder zwei_ Argumente, wobei `a` als erstes und `b` als zweites Argument dient. ### Weiterführende Quellen @@ -78,7 +78,7 @@ wobei `a` als erstes und `b` als zweites Argument dient. ```d import std.stdio : writeln; - + enum IntOps { add = 0, sub = 1, @@ -90,7 +90,7 @@ enum IntOps { Stellt eine math. Berechnung bereit Params: op = gewählte math. Operation -Returns: delegate, welches die math. +Returns: delegate, welches die math. Operation ausführt */ auto getMathOperation(IntOps op) diff --git a/basics/exceptions.md b/basics/exceptions.md index f98ff27..35459c5 100644 --- a/basics/exceptions.md +++ b/basics/exceptions.md @@ -1,6 +1,6 @@ # Exceptions -Dieser Abschnitt befasst sich nur mit Benutzer-`Exceptions` - System-`Error` sind +Dieser Abschnitt befasst sich nur mit Benutzer-`Exceptions` - System-`Error` sind normalerweise fatal und sollten __nie__ auffangen werden. ### Exceptions auffangen @@ -43,12 +43,12 @@ finally } ``` -Beachte: Die Verwendung eines [scope guards](gems/scope-guards) ist +Beachte: Die Verwendung eines [scope guards](gems/scope-guards) ist in der Regel die bessere Lösung verglichen mit dem `try-finally`-Muster. ### Benutzerdefinierte Exceptions -Eine benutzerdefinierte Exception kann durch Erben von der Klasse +Eine benutzerdefinierte Exception kann durch Erben von der Klasse `Exception` erzeugt werden: ```d @@ -83,7 +83,7 @@ abzuleiten. Es ist wichtig, Contract-Programmierung für Benutzereingaben zu vermeiden, da Contracts im Falle einer Kompilierung im Release-Mode entfernt werden. -Komfortablerweise bietet `std.exception` `enforce`, welches wie `assert` +Komfortablerweise bietet `std.exception` `enforce`, welches wie `assert` genutzt werden kann, aber `Exceptions` anstatt von `AssertError` wirft. ```d @@ -95,8 +95,8 @@ enforce(magic + 42 - magic == 42, "Floatingpoint-Arithmetik macht Spaß!"); enforce!StringException('a' != 'A', "Groß- / Kleinschreibung beachten!"); ``` -`std.exception` bietet darüberhinaus die Möglichkeit, die Behandlung -nicht-fataler Fehler mit `collect` zu verkürzen. +`std.exception` bietet darüberhinaus die Möglichkeit, die Behandlung +nicht-fataler Fehler mit `collect` zu verkürzen. ```d import std.exception : collectException; @@ -105,7 +105,7 @@ if (e) writeln("The dangerous operation failed with ", e); ``` -Der Test, ob eine Exception geworfen wurde, kann mit `assertThrown` +Der Test, ob eine Exception geworfen wurde, kann mit `assertThrown` erfolgen. ### Weiterführende Quellen @@ -129,14 +129,14 @@ void main() } catch (FileException e) { - writeln("Message:\n", e.msg); - writeln("File: ", e.file); - writeln("Line: ", e.line); - writeln("Stack trace:\n", e.info); - - // Standard-Formatierug - // auch möglich! - // writeln(e); + writeln("Message:\n", e.msg); + writeln("File: ", e.file); + writeln("Line: ", e.line); + writeln("Stack trace:\n", e.info); + + // Standard-Formatierug + // auch möglich! + // writeln(e); } } ``` diff --git a/basics/foreach.md b/basics/foreach.md index c9af096..e5dfef0 100644 --- a/basics/foreach.md +++ b/basics/foreach.md @@ -2,19 +2,19 @@ {{#img-right}}dman-teacher-foreach.jpg{{/img-right}} -Mit der `foreach`-Schleife besitzt D ein Konstrukt, das +Mit der `foreach`-Schleife besitzt D ein Konstrukt, das Iterationen besser lesbar und weniger fehlerträchtig macht. ### Elementweise Iteration -Ein gegebenes Array `arr` vom Typ `int[]` kann mithilfe der +Ein gegebenes Array `arr` vom Typ `int[]` kann mithilfe der `foreach`-Schleife elementweise iteriert werden: foreach (int e; arr) { writeln(e); } -Das erste Feld der `foreach`-Definition ist der Name der +Das erste Feld der `foreach`-Definition ist der Name der Schleifenvariable. Ihr Typ wird automatisch abgeleitet: foreach (e; arr) { @@ -22,8 +22,8 @@ Schleifenvariable. Ihr Typ wird automatisch abgeleitet: writeln(e); } -Das zweite Feld muss ein Array sein - oder ein spezielles -iterierbares Objekt, das **Range** genannt wird. Die Details +Das zweite Feld muss ein Array sein - oder ein spezielles +iterierbares Objekt, das **Range** genannt wird. Die Details werden im [nächsten Abschnitt](basics/ranges) genauer behandelt. ### Zugriff über eine Referenz @@ -31,7 +31,7 @@ werden im [nächsten Abschnitt](basics/ranges) genauer behandelt. Elemente des Arrays oder der Range werden während des Iterierens kopiert. Für Basistypen ist dies akzeptabel, für große Typen kann das aber ein Problem darstellen. Um das Kopieren zu verhindern, -oder um die Elemente *in-place* (direkt in ihren Speicherzellen) +oder um die Elemente *in-place* (direkt in ihren Speicherzellen) zu ändern, kann `ref` verwendet werden: foreach (ref e; arr) { @@ -48,13 +48,13 @@ von Iterationen, die n-mal ausgeführt werden sollen: } // 0 1 2 -Die letze Zahl in `a..b` wird von der Range ausgeschlossen -(Math.: *[a,b)*, rechtsoffenes Intervall), sodass die Schleife +Die letze Zahl in `a..b` wird von der Range ausgeschlossen +(Math.: *[a,b)*, rechtsoffenes Intervall), sodass die Schleife 3-mal ausgeführt wird. ### Iteration mit Index-Zähler -Die `foreach`-Definition kann um eine Index-Variable ergänzt +Die `foreach`-Definition kann um eine Index-Variable ergänzt werden: foreach (i, e; [4, 5, 6]) { @@ -88,7 +88,7 @@ void main() { [2, 3, 2, 3], // 10 [3, 6, 2, 9] ]; // 20 - // Iterieren des Arrays + // Iterieren des Arrays // in umgekehrter Reihenfolge import std.range: retro; foreach (row; retro(arr)) diff --git a/basics/further-reading.md b/basics/further-reading.md index b46af61..37f84a7 100644 --- a/basics/further-reading.md +++ b/basics/further-reading.md @@ -1,8 +1,8 @@ # Weitere Lektüre Nachdem du dich mit D's Grundlagen bekannt gemacht hast, kannst du -die fortgeschrittenen Abschnitte der Tour besuchen. Z.Z. sind diese -nur in der [englischsprachingen Tour](https://tour.dlang.org/) +die fortgeschrittenen Abschnitte der Tour besuchen. Z.Z. sind diese +nur in der [englischsprachingen Tour](https://tour.dlang.org/) beschrieben. Diese bietet auch eine -[Liste weiterführender Quellen in englischer Sprache](https://tour.dlang.org/tour/en/basics/further-reading) +[Liste weiterführender Quellen in englischer Sprache](https://tour.dlang.org/tour/en/basics/further-reading) an. diff --git a/basics/interfaces.md b/basics/interfaces.md index 870d247..29914db 100644 --- a/basics/interfaces.md +++ b/basics/interfaces.md @@ -8,7 +8,7 @@ Member von jeder erbenden Klasse implementiert werden müssen. void makeNoise(); } -Die `makeNoise`-Memberfunktion muss von `Dog` implementiert +Die `makeNoise`-Memberfunktion muss von `Dog` implementiert werden, weil es von dem `Animal`-Interface erbt. Letztlich verhält sich `makeNoise` wie eine `abstract`-Memberfunktion einer Basisklasse. @@ -32,8 +32,8 @@ Das [NVI-Muster](https://en.wikipedia.org/wiki/Non-virtual_interface_pattern) erlaut _non virtual_-Methoden in einem Interface, z.B. um den Aufruf überschriebener Methoden zu steuern. -D ermöglicht das NVI-Muster, indem es die Definition von `final`-Methoden in -einem Interface erlaubt, welche nicht überschrieben werden können. Dies erzwingt +D ermöglicht das NVI-Muster, indem es die Definition von `final`-Methoden in +einem Interface erlaubt, welche nicht überschrieben werden können. Dies erzwingt ein bestimmtes Verhalten, welche durch Überschreiben anderer Interface- Memberfunktionen angepasst werden kann. @@ -67,8 +67,8 @@ interface Animal { NVI-Muster. Nutzt makeNoise intern zur Anpassung des Verhaltens erbender Klassen. - - Params: + + Params: n = Anzahl der Wiederholungen */ final void multipleNoise(int n) { diff --git a/basics/loops.md b/basics/loops.md index 3a5a6bd..7e9a233 100644 --- a/basics/loops.md +++ b/basics/loops.md @@ -15,7 +15,7 @@ solange eine bestimmte Bedingung erfüllt ist: Die `do .. while`-Schleife führt den gegebenen Code-Block aus, solange eine bestimmte Bedingung erfüllt ist. Aber im Gegensatz -zur `while`-Schleife wird der Code-Block ausgeführt, bevor +zur `while`-Schleife wird der Code-Block ausgeführt, bevor die Bedingung das erste Mal geprüft wird. do { @@ -24,7 +24,7 @@ die Bedingung das erste Mal geprüft wird. ### 3) Klassische `for`-Schleife -Die klassische `for`-Schleife, bekannt aus C/C++ oder Java, +Die klassische `for`-Schleife, bekannt aus C/C++ oder Java, mit _Initialisierung_ , _Test_ und _Fortsetzung_: for (int i = 0; i < arr.length; i++) { @@ -32,7 +32,7 @@ mit _Initialisierung_ , _Test_ und _Fortsetzung_: ### 4) `foreach` -Die [`foreach`-Schleife](basics/foreach), die in der nächsten Sektion +Die [`foreach`-Schleife](basics/foreach), die in der nächsten Sektion im Detail eingeführt wird: foreach (el; arr) { @@ -64,12 +64,12 @@ Das Schlüsselwort `continue` startet die nächste Schleifen-Iteration. import std.stdio; /* -Berechnet den Mittelwert +Berechnet den Mittelwert der Elemente eines Arrays. */ double average(int[] array) { // Die Eigenschaft .empty für Arrays ist in - // D nicht nativ und wird durch Import aus + // D nicht nativ und wird durch Import aus // std.array bereitgestellt. import std.array: empty, front; diff --git a/basics/ranges.md b/basics/ranges.md index 960abec..252860a 100644 --- a/basics/ranges.md +++ b/basics/ranges.md @@ -21,10 +21,10 @@ for (auto __rangeCopy = range; } ``` -Falls das Range-Objekt ein Referenztyp ist (z.B. `class`), wird es -verbraucht und ist daher für weitere Iterationen nicht mehr verfügbar +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 +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`), @@ -32,7 +32,7 @@ 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 +Jedes Objekt mit folgendem Interface wird **Range** genannt und kann somit iteriert werden: ``` @@ -43,17 +43,17 @@ somit iteriert werden: void popFront(); } ``` - + Beachte: Obwohl `empty` und `front` gewöhnlich als `const`-Funktionen -definiert werden (was impliziert, dass ein Aufruf die Range nicht +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 +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 +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. ### Übungsaufgabe @@ -77,7 +77,7 @@ struct FibonacciRange { bool empty() const @property { - // Wann endet die + // Wann endet die // Fibonacci-Folge?! } @@ -98,12 +98,12 @@ void main() FibonacciRange fib; // `take` erstellt eine weitere Range, - // die maximal N Elemente zurückgibt. + // die maximal N Elemente zurückgibt. // Diese Range ist _lazy_ und nutzt // die Original-Range nur wenn nötig. auto fib10 = take(fib, 10); - // In diesem Fall werden alle Elemente + // In diesem Fall werden alle Elemente // genutzt und die Range in ein Array // aus Integer-Werten konvertiert. int[] the10Fibs = array(fib10); diff --git a/basics/templates.md b/basics/templates.md index e84cff5..2b98b12 100644 --- a/basics/templates.md +++ b/basics/templates.md @@ -1,7 +1,7 @@ # Templates **D** erlaubt die Defintion von Funktionstemplates wie C++ und -Java. Dies ist ein Mittel um **generische** Funktionen oder Objekte zu +Java. Dies ist ein Mittel um **generische** Funktionen oder Objekte zu definieren, die für jeden Typ funktionieren, der mit den Anweisungen des Funktionsrumpfs kompiliert: @@ -10,8 +10,8 @@ Funktionsrumpfs kompiliert: } Der Template-Parameter `T` wird in runden Klammern vor den eigentlichen -Funktionsparametern definiert. `T` ist ein Platzhalter, der vom Compiler -ersetzt wird, wenn die Funktion tatsächlich mit dem `!`-Operator +Funktionsparametern definiert. `T` ist ein Platzhalter, der vom Compiler +ersetzt wird, wenn die Funktion tatsächlich mit dem `!`-Operator *instanziiert* wird: add!int(5, 10); @@ -19,28 +19,28 @@ ersetzt wird, wenn die Funktion tatsächlich mit dem `!`-Operator add!Animal(dog, cat); // kompiliert nicht! Animal implementiert + nicht ### Implizite Template-Parameter - -Funktionstemplates haben zwei Parametersätze - den ersten für + +Funktionstemplates haben zwei Parametersätze - den ersten für Kompilierzeit-Argumente und den zweiten für Laufzeit-Argumente. (Normale Funktionen akzeptieren nur Laufzeit-Argumente). -Wenn ein oder mehrere Kompilierzeit-Argumente beim Aufruf ausgelassen +Wenn ein oder mehrere Kompilierzeit-Argumente beim Aufruf ausgelassen werden, versucht der Compiler deren Typ aus der Liste der Laufzeit- Argumente abzuleiten. int a = 5; int b = 10; add(a, b); // T wird abgeleitet zu `int` float c = 5.0; - add(a, c); // T wird abgeleitet zu `float` + add(a, c); // T wird abgeleitet zu `float` ### Template-Eigenschaften Eine Funktion kann beliebig viele Template-Parameter besitzen, welche -während der Instanziierung mit der `func!(T1, T2 ..)`-Syntax spezifiziert -werden. Template-Parameter können von jedem Basistyp sein (inkl. `string` +während der Instanziierung mit der `func!(T1, T2 ..)`-Syntax spezifiziert +werden. Template-Parameter können von jedem Basistyp sein (inkl. `string` und Fließkommatypen). -Anders als Generics in Java, beziehen sich Templates in D nur auf die -Kompilierzeit. Dies führt zu hochoptimiertem Code maßgeschneidert für die +Anders als Generics in Java, beziehen sich Templates in D nur auf die +Kompilierzeit. Dies führt zu hochoptimiertem Code maßgeschneidert für die beim Funktionsaufruf genutzten Typen. Natürlich können auch die Typen `struct`, `class` und `interface` als Template diff --git a/basics/type-qualifiers.md b/basics/type-qualifiers.md index 0a6a708..7cd04e9 100644 --- a/basics/type-qualifiers.md +++ b/basics/type-qualifiers.md @@ -41,7 +41,7 @@ verarbeiten. // if not commented out, next line will // result in error (can't modify const): // s[0] = 'x'; - + import std.stdio : writeln; writeln(s); } diff --git a/gems/attributes.md b/gems/attributes.md index 3a334b5..c48fe99 100644 --- a/gems/attributes.md +++ b/gems/attributes.md @@ -1,8 +1,8 @@ # Attribute -Funktionen können in D auf verschiedene Arten mit +Funktionen können in D auf verschiedene Arten mit Attributen versehen werden. Im folgenden werden -zwei spracheigene und die benutzerdefinierten +zwei spracheigene und die benutzerdefinierten Attribute näher beleuchtet. Darüber hinaus gibt es `@safe`, `@system` und `@trusted`, die bereits im ersten Kapitel erwähnt wurden. @@ -16,16 +16,16 @@ die äußere Welt wie ein normaler Member aus. @property bar() { return 10; } @property bar(int x) { writeln(x); } } - + Foo foo; writeln(foo.bar); // ruft tatsächlich foo.bar() auf foo.bar = 10; // calls foo.bar(10); - + ### `@nogc` Wenn der D-Compiler auf eine als `@nogc` markierte Funktion trifft, wird sichergestellt, dass **keine** Speicherallokationen -im Kontext dieser Funktion vorgenommen werden. Eine +im Kontext dieser Funktion vorgenommen werden. Eine `@nogc`-Funktion darf nur Funktionen aufrufen, die ihrerseits als `@nogc` markiert sind. @@ -36,11 +36,11 @@ als `@nogc` markiert sind. ### Benutzerdefinierte Attribute (UDAs) -Jede Funktion und jeder Typ in D kann mit benutzerdefinierten +Jede Funktion und jeder Typ in D kann mit benutzerdefinierten Attributen (engl: user-defined attributes, UDAs) versehen werden: struct Bar { this(int x) {} } - + struct Foo { @("Hello") { @Bar(10) void foo() { @@ -51,12 +51,12 @@ Attributen (engl: user-defined attributes, UDAs) versehen werden: Jeder Typ, spracheigen oder benutzerdefiniert, kann Funktionen als Attribut beigefügt werden. -Die Funktion `foo()` in diesem Beispiel besitzt die -Attribute `"Hello"` vom Typ `string` und `Bar` von Typ -`Bar` mit dem Wert `10`. Zugang zu den Attributen -bieten die im Compiler integrierten *Traits* +Die Funktion `foo()` in diesem Beispiel besitzt die +Attribute `"Hello"` vom Typ `string` und `Bar` von Typ +`Bar` mit dem Wert `10`. Zugang zu den Attributen +bieten die im Compiler integrierten *Traits* (dt.: Merkmale / Eigenschaften) mittels -`__traits(getAttributes, Foo)`, die einen +`__traits(getAttributes, Foo)`, die einen [`TypeTuple`](https://dlang.org/phobos/std_typetuple.html) zurückgeben. diff --git a/gems/bit-manipulation.md b/gems/bit-manipulation.md index ff5d6ad..84bf771 100644 --- a/gems/bit-manipulation.md +++ b/gems/bit-manipulation.md @@ -1,6 +1,6 @@ # Bitmanipulation -Ein sehr gutes Beispiel für die Erzeugung von Code zur +Ein sehr gutes Beispiel für die Erzeugung von Code zur Kompilierzeit in D mit Mixins ist Bitmanipulation. ### Einfache Bitmanipulation @@ -14,10 +14,10 @@ D bietet die folgenden Operatoren für Bitmanipulation: - `>>` bitweise vorzeichenbewahrende Rechts-Verschiebung - `>>>` bitweise vorzeichenlose Rechts-Verschiebung -### Ein praktisches Beispiel +### Ein praktisches Beispiel Ein gängiges Beispiel für Bitmanipulation ist das Einlesen -eines Bitwertes. D bietet `core.bitop.bt` für die meisten +eines Bitwertes. D bietet `core.bitop.bt` für die meisten allgemeinen Aufgaben. Nichtsdestotrotz werden wir zum besseren Verständnis die ausführliche Implementierung eines Bit-Tests vornehmen: @@ -31,10 +31,10 @@ bool getFieldA() } ``` -Eine Verallgemeinerung ist der Test von Bitblöcken, -die aus mehreren aneinandergereihten Bits bestehen. -Dazu wird eine spezielle Maske der Länge des Blocks -benötigt. Der Datenblock wird entsprechend verschoben, +Eine Verallgemeinerung ist der Test von Bitblöcken, +die aus mehreren aneinandergereihten Bits bestehen. +Dazu wird eine spezielle Maske der Länge des Blocks +benötigt. Der Datenblock wird entsprechend verschoben, bevor die Maske angewendet wird: ```d @@ -47,7 +47,7 @@ uint getFieldA() } ``` -Das Setzen solch eines Blocks kann genauso durch +Das Setzen solch eines Blocks kann genauso durch Negation der Maske erreicht werden, wodurch das Schreiben nur innerhalb des spezifizierten Blocks möglich ist: @@ -58,19 +58,19 @@ void setFieldA(bool b); } ``` -## Les- und Wartbarkeit mit `std.bitmanip` +## Les- und Wartbarkeit mit `std.bitmanip` -D bietet einen kompletten Werkzeugkoffer zur Erzeugung +D bietet einen kompletten Werkzeugkoffer zur Erzeugung benutzerdefinierter Bitmanipulationen. Allerdings ist -bitmanipulierender Code oft fehlerträchtig und schwer +bitmanipulierender Code oft fehlerträchtig und schwer zu warten. -`std.bitmanip` schafft hier Abhilfe, indem es (unter -Zuhilfenahme von Mixins) erlaubt, wartbare, leicht -lesbare Bitmanipulationen zu schreiben - ohne dabei +`std.bitmanip` schafft hier Abhilfe, indem es (unter +Zuhilfenahme von Mixins) erlaubt, wartbare, leicht +lesbare Bitmanipulationen zu schreiben - ohne dabei Performance zu opfern. -Das Beispiel zeigt eine `BitVector`-Definition, die, -obwohl sie nur X Bits benutzt, kaum von regulären Strukturen +Das Beispiel zeigt eine `BitVector`-Definition, die, +obwohl sie nur X Bits benutzt, kaum von regulären Strukturen unterscheidbar ist. `std.bitmanip` und `core.bitop` beinhalten weitere Helfer, @@ -79,10 +79,10 @@ eines geringen Speicherverbrauchs stellen. ### Auffüllen und Ausrichtung -Der Compiler wird Variablen auffüllen (engl.: Padding), die -eine Größe kleiner als das Speicherlayout des Betriebssystems +Der Compiler wird Variablen auffüllen (engl.: Padding), die +eine Größe kleiner als das Speicherlayout des Betriebssystems (`size_t.sizeof`) besitzen, wie z.B. `bool`, `byte` oder `char`. -Daher wird die Ausrichtung (engl.: Allignment) der Felder +Daher wird die Ausrichtung (engl.: Allignment) der Felder beginnend bei Bit 0 empfohlen. ## Weiterführende Quellen @@ -96,7 +96,7 @@ beginnend bei Bit 0 empfohlen. struct BitVector { import std.bitmanip : bitfields; - // erzeugt ein private Feld mit + // erzeugt ein private Feld mit // folgenden Proxies mixin(bitfields!( uint, "x", 2, @@ -122,14 +122,14 @@ void main() // 4 Byte (int) pro Variable writeln(Vector.sizeof); - struct BadVector - { - bool a; - int x, y, z; - bool b; - } - // wegen des Auffüllens (padding) - // werden 4 Byte pro Feld verwendet - writeln(BadVector.sizeof); + struct BadVector + { + bool a; + int x, y, z; + bool b; + } + // wegen des Auffüllens (padding) + // werden 4 Byte pro Feld verwendet + writeln(BadVector.sizeof); } ``` diff --git a/gems/compile-time-function-evaluation-ctfe.md b/gems/compile-time-function-evaluation-ctfe.md index 4d277d0..f3d4e26 100644 --- a/gems/compile-time-function-evaluation-ctfe.md +++ b/gems/compile-time-function-evaluation-ctfe.md @@ -1,35 +1,35 @@ # Compile Time Function Evaluation (CTFE) -CTFE (dt.: Funktionsauswertung zur Kompilierzeit) ist ein +CTFE (dt.: Funktionsauswertung zur Kompilierzeit) ist ein Mechanismus, der dem Compiler die Ausführung von Funktionen zur **Kompilierzeit** ermöglicht. Für den Gebrauch dieses -Features sind es keine speziellen D-Befehle nötig - -immer, wenn eine Funktion nur auf zur Kompilierzeit -bekannten Werten beruht, kann der D-Compiler die -Entscheidung treffen, die Funktion schon während der +Features sind es keine speziellen D-Befehle nötig - +immer, wenn eine Funktion nur auf zur Kompilierzeit +bekannten Werten beruht, kann der D-Compiler die +Entscheidung treffen, die Funktion schon während der Kompilierung auszuwerten. - // Ergebnis wird zur Kompilierzeit - // berechnet. Der erzeugte Machinencode + // Ergebnis wird zur Kompilierzeit + // berechnet. Der erzeugte Machinencode // enthält keinen Funktionsaufruf! static val = sqrt(50); Schlüsselwörter wie `static`, `immutable` oder `enum` -weisen den Compiler an, CTFE zu nutzen, wann immer -dies möglich ist. Großartig daran ist, dass die Funktion +weisen den Compiler an, CTFE zu nutzen, wann immer +dies möglich ist. Großartig daran ist, dass die Funktion nicht neu geschrieben werden muss: int n = berechneZurLaufzeit(); - // Die gleiche Funktion wie oben, aber + // Die gleiche Funktion wie oben, aber // dieses Mal klassisch zur Laufzeit // ausgeführt. auto val = sqrt(n); -Ein herausragendes Beispiel in D ist die +Ein herausragendes Beispiel in D ist die [std.regex](https://dlang.org/phobos/std_regex.html)-Bibliothek. -Sie bietet einen `ctRegex`-Typ (ct: compile time), der +Sie bietet einen `ctRegex`-Typ (ct: compile time), der *String Mixins* und CTFE nutzt, um während der Kompilierung -hochoptimierte Reguläre Ausdrücke zu generieren. +hochoptimierte Reguläre Ausdrücke zu generieren. Die Laufzeitvariante `regex` nutzt die gleiche Codebasis. auto ctr = ctRegex!(`^.*/([^/]+)/?$`); @@ -37,8 +37,8 @@ Die Laufzeitvariante `regex` nutzt die gleiche Codebasis. // ctr und tr liefern das gleiche Ergebnis, // nur dass ctr schneller ist! -Nicht alle Sprachfeatures stehen für CTFE zur Verfügung. -Allerdings wird die Menge derunterstützten Features ständig +Nicht alle Sprachfeatures stehen für CTFE zur Verfügung. +Allerdings wird die Menge derunterstützten Features ständig erweitert. ### Weiterführende Quellen @@ -57,8 +57,8 @@ Berechnet die Quadratwurzel einer Zahl mithilfe des Newton'schen Approximationsschemas. Params: x = Wurzelargument - -Returns: Quadratwurzel von x + +Returns: Quadratwurzel von x */ auto sqrt(T)(T x) { // Epsilon ist Abbruchgrenze der diff --git a/gems/contract-programming.md b/gems/contract-programming.md index 940b8e3..06f387c 100644 --- a/gems/contract-programming.md +++ b/gems/contract-programming.md @@ -1,20 +1,20 @@ # Contract Programming Contract Programming (dt.: vertragsbasierte Programmierung) -in D umfasst einen Satz von Sprachkonstrukten, die die +in D umfasst einen Satz von Sprachkonstrukten, die die Quellcodequalität durch bestimmte Prüfroutinen (sog. Contracts) erhöhen und korrektes Verhalten sicherstellen sollen. -Contracts stehen nur im **Debug-Modus** zur Verfügung und +Contracts stehen nur im **Debug-Modus** zur Verfügung und werden im Release-Modus nicht ausgeführt. -Deshalb sollten sie nicht für die Überprüfung von +Deshalb sollten sie nicht für die Überprüfung von Benutzereingaben eingesetzt werden. ### `assert` -Der `assert(...)`-Ausdruck bietet die einfachste Form +Der `assert(...)`-Ausdruck bietet die einfachste Form des Contract Programming und prüft ob eine bestimmte -Bedingung eingehalten wird, anderenfalls bricht das +Bedingung eingehalten wird, anderenfalls bricht das Programm ab. assert(sqrt(4) == 2); @@ -38,14 +38,14 @@ Eingangsparameter und Rückgabewerte von Funktionen. Der Inhalt des `in`-Blocks könnte auch im Rumpf der Funktion stehen, auf diese Art wird aber die Intention viel klarer. -Im `out`-Block der Funktion kann der Rückgabewert mit +Im `out`-Block der Funktion kann der Rückgabewert mit `out(result)` erfasst und entsprechend geprüft werden. ### Überprüfung mit invariant `invariant()` ist eine spezielle Memberfunktion von `struct`- und `class`-Typen, der die Überprüfung des Objektzustands -während dessen gesamter Lebenszeit erlaubt. +während dessen gesamter Lebenszeit erlaubt. `invariant()` wird zu folgenden Zeitpunkten ausgeführt: * nach Ausführung des Konstruktors @@ -103,10 +103,10 @@ struct Date { /** Serialisiere Datenobjekt aus einem YYYY-MM-DD String. - + Params: date = zu serialisiernder String - + Returns: Datnobjekt /* void fromString(string date) @@ -156,7 +156,7 @@ void main() { // Dies wird invariant fehlschlagen lassen. // Überprüfe Benutzereingaben nicht mit - // Contracts! + // Contracts! // Stattdessen Exceptions werfen! date.fromString("2016-13-7"); diff --git a/gems/documentation.md b/gems/documentation.md index 7524d91..5b07e64 100644 --- a/gems/documentation.md +++ b/gems/documentation.md @@ -1,8 +1,8 @@ # Dokumentation -D möchte wichtige Bereiche moderner Softwareentwicklung -in die Sprache integrieren. Neben *Contract Programming* -und *Unittests* bietet D nativ die Erzeugung einer +D möchte wichtige Bereiche moderner Softwareentwicklung +in die Sprache integrieren. Neben *Contract Programming* +und *Unittests* bietet D nativ die Erzeugung einer [Quellcode-Dokumentation](https://dlang.org/phobos/std_variant.html) an. Für die Dokumentation von Typen und Funktionen wird @@ -37,9 +37,9 @@ standartisierte Dokumentationsabschnitte. Hier könnte ein längerer Absatz mit einer detailierten Beschreibung der großartigen und für den Fortbestand - der Menschheit entscheidenden + der Menschheit entscheidenden Implikationen, die die Berechnung der - Quadratwurze zweifelsohne besitzt, + Quadratwurze zweifelsohne besitzt, stehen. Beispiel: @@ -47,7 +47,7 @@ standartisierte Dokumentationsabschnitte. double sq = sqrt(4); ------------------- Params: - number = die Zahl, die quadriert + number = die Zahl, die quadriert werden soll License: use freely for any purpose Throws: es wird nicht geworfen diff --git a/gems/functional-programming.md b/gems/functional-programming.md index 206c387..adf7952 100644 --- a/gems/functional-programming.md +++ b/gems/functional-programming.md @@ -1,13 +1,13 @@ # Funktionale Programmierung D legt besonderen Wert auf *funktionale Programmierung* und -bietet erstklassige Unterstützung für die Entwicklung im -funktionalen Stil. +bietet erstklassige Unterstützung für die Entwicklung im +funktionalen Stil. In D kann eine Funktion als `pure` (dt.: rein, pur) deklariert -werden, und so anzeigen, dass für eine Eingabe eine Ausgabe +werden, und so anzeigen, dass für eine Eingabe eine Ausgabe generiert wird, die nur von dieser abhängig ist. Reine (`pure`) Funktionen dürfen weder auf globale veränderliche Zustände -zugreifen noch diese verändern und dürfen selbst nur Funktionen +zugreifen noch diese verändern und dürfen selbst nur Funktionen aufrufen, die ihrerseits als `pure` markiert sind. int add(int lhs, int rhs) pure { @@ -16,8 +16,8 @@ aufrufen, die ihrerseits als `pure` markiert sind. } Diese Variante von `add` wird als **starke reine Funktion** -(engl.: strongly pure function) bezeichnet, weil ihr -zurückgegebenes Ergebnis nur von den Eingangsparametern +(engl.: strongly pure function) bezeichnet, weil ihr +zurückgegebenes Ergebnis nur von den Eingangsparametern abhängt, ohne diese zu modifizieren. D erlaubt auch **schwache reine Funktionen** (engl.: weakly pure function), die veränderliche Parameter haben können: @@ -26,18 +26,18 @@ die veränderliche Parameter haben können: result = lhs + rhs; } -Diese Funktionen werden immer noch als rein angenommen und +Diese Funktionen werden immer noch als rein angenommen und können auf globale veränderliche Zustande weder zugreifen -noch diese ändern. Nur Eingangsparameter dürfen Änderungen +noch diese ändern. Nur Eingangsparameter dürfen Änderungen erfahren. Wegen der durch `pure` auferlegten Einschränkungen eignen -sich reine Funktionen ideal für Multithreading-Umgebungen +sich reine Funktionen ideal für Multithreading-Umgebungen zur Vermeidung von Data-Racing. Auch können reine Funktionen -einfach zwischengespeichert werden und erlauben eine Reihe +einfach zwischengespeichert werden und erlauben eine Reihe von Optimierungen durch den Compiler. -Das Attribut `pure` wird für Template-Funktionen und -`auto`-Funktionen automatisch vom Compiler abgeleitet +Das Attribut `pure` wird für Template-Funktionen und +`auto`-Funktionen automatisch vom Compiler abgeleitet (dies gilt auch für `@safe`, `nothrow` und `@nogc`). ### Weiterführende Quellen @@ -72,9 +72,9 @@ void main() import std.functional : memoize; import std.stdio : writefln, writeln; - - // memoize speichert das Ergebnis des - // Funktionaufrufs abhängig vom den + + // memoize speichert das Ergebnis des + // Funktionaufrufs abhängig vom den // Eingangsparametern zwischen. // Reine Funktionen sind dafür bestens // geeignet! @@ -88,7 +88,7 @@ void main() foreach (i; 0 .. 10) benchmark!test(1)[0] - .to!("msecs", double) - .writeln("brauchte: Millisekunden"); + .to!("msecs", double) + .writeln("brauchte: Millisekunden"); } ``` diff --git a/gems/opdispatch-opapply.md b/gems/opdispatch-opapply.md index a3cc814..729b88f 100644 --- a/gems/opdispatch-opapply.md +++ b/gems/opdispatch-opapply.md @@ -8,7 +8,7 @@ Im Folgenden werden die beiden speziellen Operatorüberladungen ### opDispatch -`opDispatch` kann als Memberfunktionen eines `struct`- +`opDispatch` kann als Memberfunktionen eines `struct`- oder `class`-Typs definiert werden. Jede unbekannte Memberfunktionsaufruf für diesen Typ wird an `opDispatch` weitergeleitet, wobei sowohl Name als auch Parameter der unbekannten @@ -79,7 +79,7 @@ https://dlang.org/phobos/std_variant.html import std.variant : Variant; /* -Typ, der mittels opDispatch mit jeder +Typ, der mittels opDispatch mit jeder Anzahl an Membern gefüllt werden kann. Wie JavaScript's var. */ diff --git a/gems/range-algorithms.md b/gems/range-algorithms.md index dcea8c6..25f29b3 100644 --- a/gems/range-algorithms.md +++ b/gems/range-algorithms.md @@ -3,7 +3,7 @@ Die Standardmodule [std.range](http://dlang.org/phobos/std_range.html) und [std.algorithm](http://dlang.org/phobos/std_algorithm.html) bieten eine Vielfalt an großartigen Funktionen, die, basierend -auf *Ranges* als Grundbausteinen, zu komplexen Operationen +auf *Ranges* als Grundbausteinen, zu komplexen Operationen zusammengefügt werden konnen, und dies auf eine lesbare Art und Weise. Das Großartige an diesen Algorithmen ist die Anwendbarkeit @@ -32,18 +32,18 @@ definierten Prädikats: theBigBigRange.take(10); -`zip` - Iteriert parallel über zwei Ranges und bindet die Elemente +`zip` - Iteriert parallel über zwei Ranges und bindet die Elemente zu Tupeln: assert(zip([1,2], ["hello","world"]).front == tuple(1, "hello")); -`generate` - Erzeugt eine Range basierend auf einer Funktion, +`generate` - Erzeugt eine Range basierend auf einer Funktion, die bei jedem Iterationsschritt aufgerufen wird, z.B.: alias RandomRange = generate!(x => uniform(1, 1000)); -`cycle` - Erzeugt eine Range, welche die gegebene Eingangs-Range +`cycle` - Erzeugt eine Range, welche die gegebene Eingangs-Range für immer wiederholt: auto c = cycle([1]); @@ -68,7 +68,7 @@ import std.string : format; void main() { - string text = "Diese Tour gibt dir + string text = "Diese Tour gibt dir einen Überblick über diese mächtige und ausdrucksstarke Programmiersprache, die direkt zu effizientem, *nativem* @@ -83,8 +83,8 @@ void main() auto wordCharCounts = words .map!"a.count"; - // Ausgabe der Zeichenanzahl - // pro Word beginnend mit + // Ausgabe der Zeichenanzahl + // pro Word beginnend mit // der geringsten Anzahl! zip(wordCharCounts, words) // Array-Konvertierung für's Sortieren @@ -92,10 +92,10 @@ void main() .sort() // keine Kopie nötig! .uniq() - // Alle Wörter mit gleicher Anzahl + // Alle Wörter mit gleicher Anzahl // an Zeichen in eine Reihe: // chunkBy generiert eine Range aus - // Ranges, getrennt durch die + // Ranges, getrennt durch die // Länge .chunkBy!(a => a[0]) // Diese Elemente werden verbunden diff --git a/gems/scope-guards.md b/gems/scope-guards.md index 11120d3..c9a40b5 100644 --- a/gems/scope-guards.md +++ b/gems/scope-guards.md @@ -5,19 +5,19 @@ von Anweisungen unter bestimmten Bedingungen, wenn der aktuelle Codeblock verlassen wird: * `scope(exit)` wird die Anweisungen immer ausführen -* `scope(success)` Anweisungen werden ausgeführt, wenn keine +* `scope(success)` Anweisungen werden ausgeführt, wenn keine Exception geworfen wurde * `scope(failure)` Anweisungen werden ausgeführt, wenn vor dem Blockende eine Exception geworfen wurde -Die Verwendung von Scope Guards erhöht die Klarheit des Quellcodes, weil +Die Verwendung von Scope Guards erhöht die Klarheit des Quellcodes, weil so Ressourcenallokation und Aufräum-Code nebeneinander stehen können. Auch kann sichergestellt werden, dass bestimmter Code *immer* ausgeführt wird, unabhängig davon, ob zur Laufzeit ein Fehler auftritt oder nicht. -D's `scope` bietet einen effektiven Ersatz für das in C++ -verwendete RAII-Idiom, dass oft zur Implementierung spezieller Scope +D's `scope` bietet einen effektiven Ersatz für das in C++ +verwendete RAII-Idiom, dass oft zur Implementierung spezieller Scope Guard-Objekte für spezielle Ressourcen führt. Scope Guards werden in umgekehrter Reihenfolge ihrer Defintion ausgeführt. diff --git a/gems/string-mixins.md b/gems/string-mixins.md index 7b4e81b..b8e0d98 100644 --- a/gems/string-mixins.md +++ b/gems/string-mixins.md @@ -1,7 +1,7 @@ # String Mixins -Der `mixin`-Ausdruck nimmt einen beliebigen String, -kompiliert diesen und generiert dementsprechende +Der `mixin`-Ausdruck nimmt einen beliebigen String, +kompiliert diesen und generiert dementsprechende Befehle. Dies ist ein reiner **Kompilierzeit**-Mechanismus, der nur mit Strings arbeitet, die während der Kompilierung zur Verfügung stehen. @@ -15,7 +15,7 @@ beruhen. Die Kombination von `mixin` und **CTFE** erlauben beeindruckende Bibliotheken wie [Pegged](https://github.com/PhilippeSigaud/Pegged), -die einen Grammatik-Parser aus einer als String definierten +die einen Grammatik-Parser aus einer als String definierten Grammatik im Quellcode erzeugt. ### Weiterführende Quellen @@ -37,7 +37,7 @@ void main() // 'Hello World' mal anders! mixin(`writeln("Hello World");`); - // Reiche den gewünschten Operator + // Reiche den gewünschten Operator // als Template-Parameter weiter. writeln("5 + 12 = ", calculate!"+"(5,12)); writeln("10 - 8 = ", calculate!"-"(10,8)); diff --git a/gems/subtyping.md b/gems/subtyping.md index cf7543f..c52aa12 100644 --- a/gems/subtyping.md +++ b/gems/subtyping.md @@ -1,11 +1,11 @@ # Subtypen in D -D bietet Vererbung für Klassen an, nicht aber für -`struct`s. Zur Erweiterung der Funktionalität von +D bietet Vererbung für Klassen an, nicht aber für +`struct`s. Zur Erweiterung der Funktionalität von `struct`s gibt es allerdings ein weiteres großartiges Mittel: Die Verwendung von Subtypen. -Einer der Member eines `struct`s kann als +Einer der Member eines `struct`s kann als `alias this` definiert werden: struct SafeInt { @@ -14,15 +14,15 @@ Einer der Member eines `struct`s kann als } Jede Funktion oder Operation an `SafeInt`s, -die nicht vom Typ selbst behandelt werden kann, +die nicht vom Typ selbst behandelt werden kann, wird an den `alias this`-Member weitergeleitet. -Von außen betrachtet ist `SafeInt` also ein +Von außen betrachtet ist `SafeInt` also ein normaler Integer. Dies erlaubt die Erweiterung anderer Typen mit neuer Funktionalität, aber ohne Mehraufwand (engl.: zero overhead) in Bezug auf Speicher oder -Laufzeit. +Laufzeit. `alias this` funktioniert auch mit Klassen! @@ -44,7 +44,7 @@ struct Vector3 { void main() { Vector3 vec; - // Wir interagieren hier im + // Wir interagieren hier im // Wesentlichen mit double[]. vec = [ 0.0, 1.0, 0.0 ]; assert(vec.length == 3); diff --git a/gems/template-meta-programming.md b/gems/template-meta-programming.md index b987483..f4f007c 100644 --- a/gems/template-meta-programming.md +++ b/gems/template-meta-programming.md @@ -28,7 +28,7 @@ auswertet: } Wenn die Bedingung erfüllt ist, wird der Inhalt des Blocks -kopiert. Die umschließenden Klammern werden weggelassen +kopiert. Die umschließenden Klammern werden weggelassen und kein weiterer Scope erstellt. Ein neuer Block / Scope kann allerdings explizit mit `{ {` und `} }` erzeugt werden. @@ -38,7 +38,7 @@ Funktionen, im globalen Scope sowie in Typdefinitionen. ### `mixin template` -Überall, wo Textbausteine gefragt sind, können +Überall, wo Textbausteine gefragt sind, können `mixin template`s genutzt werden: mixin template Foo(T) { @@ -49,8 +49,8 @@ Funktionen, im globalen Scope sowie in Typdefinitionen. `mixin template` können eine beliebige Anzahl an komplexen Ausdrücken enthalten und werden am Instanziierungspunkt -eingesetzt. -Dies macht einen Präprozessor, wie ihn z.B. C und C++ +eingesetzt. +Dies macht einen Präprozessor, wie ihn z.B. C und C++ besitzen, überfüssig. ### Template-Beschränkungen @@ -107,9 +107,9 @@ private: /* Generator für Getter und Setter zur - Vermeidung sich wiederholender + Vermeidung sich wiederholender Textbausteine (engl.:boiler plate)! - + var -> T getVAR() and void setVAR(T) */ mixin template GetterSetter(string var) { @@ -132,7 +132,7 @@ private: public: /* - Die dot-Funktion ist nur für + Die dot-Funktion ist nur für Floatingpoint-Typen verfügbar. */ static if (isFloatingPoint!T) { @@ -146,16 +146,16 @@ public: void main() { auto vec = Vector3!double(3,3,3); - // Folgendes funktioniert aufgrund der + // Folgendes funktioniert aufgrund der // Template-Beschränkung nicht! // Vector3!string illegal; auto vec2 = Vector3!double(4,4,4); writeln("vec dot vec2 = ", vec.dot(vec2)); - + auto vecInt = Vector3!int(1,2,3); // besitzt die Function dot nicht, da diese - // statisch nur für Floatingpoint-Typen + // statisch nur für Floatingpoint-Typen // definiert wurde // vecInt.dot(Vector3!int(0,0,0)); diff --git a/gems/traits.md b/gems/traits.md index ef1349a..360ea16 100644 --- a/gems/traits.md +++ b/gems/traits.md @@ -1,6 +1,6 @@ # Traits -Eine von D's Stärken ist CTFE (Funktionsauswertung zur +Eine von D's Stärken ist CTFE (Funktionsauswertung zur Kompilierzeit), die, kombiniert mit Introspektion, das Schreiben hoch optimierter generischer Programme möglich macht. @@ -15,8 +15,8 @@ verarbeiten: S[] splitIntoWord(S)(S input) if (isSomeString!S) ``` -Dies gilt auch für Template-Parameter. Z.B. kann -`myWrapper` sicherstellen, dass das hineingereichte +Dies gilt auch für Template-Parameter. Z.B. kann +`myWrapper` sicherstellen, dass das hineingereichte Symbol eine aufrufbare Funktion ist: ```d @@ -25,7 +25,7 @@ if (isCallable!f) ``` Als einfaches Beispiel wird die Funktion [`commonPrefix`](https://dlang.org/phobos/std_algorithm_searching.html#.commonPrefix) -aus `std.algorithm.searching` analysiert, +aus `std.algorithm.searching` analysiert, die das gemeinsame Präfix zweier Ranges ausgibt: ```d @@ -36,29 +36,29 @@ if (isForwardRange!R1 !isNarrowString!R1) ``` -Dies bedeutet, dass die Funktion nur aufrufbar ist und +Dies bedeutet, dass die Funktion nur aufrufbar ist und damit kompiliert, wenn: - `r1` speicherbar ist (garantiert durch `isForwardRange`) - `r2` iterierbar ist (garantiert durch `isInputRange`) - `pred` mit den Elementtypen von `r1` und `r2` aufrufbar ist -- `r1` kein narrow string (`char[]`, `string`, `wchar` or `wstring`) +- `r1` kein narrow string (`char[]`, `string`, `wchar` or `wstring`) ist - der Einfachheit halber, sonst müsste eine Dekodierung stattfinden ### Spezialisierung -Viele APIs zielen auf Vielseitigkeit, allerdings soll +Viele APIs zielen auf Vielseitigkeit, allerdings soll die Verallgemeinerung nicht mit zusätzlicher Laufzeit erkauft werden. Mit Introspektion und CTFE ist es möglich, eine Funktion -zur Kompilierzeit zu spezialisieren, um so die beste +zur Kompilierzeit zu spezialisieren, um so die beste Performance für gegebene Eingangstypen zu erreichen. Ein gängiges Problem ist, dass die Länge eines Streams oder einer Liste, im Gegensatz zu z.B. einem Array, vor -dem Durchschreiten nicht bekannt ist. -Das folgende Beispiel stellt eine einfache Implementation -der `std.range`-Methode `walkLength` dar, die das +dem Durchschreiten nicht bekannt ist. +Das folgende Beispiel stellt eine einfache Implementation +der `std.range`-Methode `walkLength` dar, die das Durchschreiten für alle iterierbaren Typen verallgemeinert: ```d @@ -70,15 +70,15 @@ else #### `commonPrefix` -Die Verwendung von Introspektion zur Kompilierzeit ist -in Phobos allgegenwärtig. Z.B. differenziert `commonPrefix` +Die Verwendung von Introspektion zur Kompilierzeit ist +in Phobos allgegenwärtig. Z.B. differenziert `commonPrefix` zwischen `RandomAccessRange`s und linear iterierbaren Ranges, -da es in einer `RandomAccessRange` möglich ist zwischen +da es in einer `RandomAccessRange` möglich ist zwischen Positionen zu springen und so den Algorithmus zu beschleunigen. #### Weitere CTFE-Magie -[std.traits](https://dlang.org/phobos/std_traits.html) umfasst die +[std.traits](https://dlang.org/phobos/std_traits.html) umfasst die meisten von D's [traits](https://dlang.org/spec/traits.html). #### Spezielle Schlüsselwörter diff --git a/gems/unicode.md b/gems/unicode.md index d57337f..1470de5 100644 --- a/gems/unicode.md +++ b/gems/unicode.md @@ -1,13 +1,13 @@ # Unicode in D -Unicode ist ein globaler Standard für Textkodierung und --repräsentation in Computern. D unterstützt Unicode -vollständig, sowohl sprachseitig als auch in der +Unicode ist ein globaler Standard für Textkodierung und +-repräsentation in Computern. D unterstützt Unicode +vollständig, sowohl sprachseitig als auch in der Standard-Bibliothek. ## Warum Unicode? -Computer haben auf der niedrigsten Ebene keinen Begriff +Computer haben auf der niedrigsten Ebene keinen Begriff davon, was ein Text ist, da sie nur mit Zahlen umgehen. Daher braucht es eine Möglichkeit, Textdaten in eine binäre Repräsentation zu wandeln und umgekehrt. Die Methode @@ -21,7 +21,7 @@ Unicode ist einzigartig, weil es alle Sprachen der Welt in einem Kodierungsschema repräsentiert. Vor Unicode war eine Kommunikation zwischen Computern unterschiedlicher Hersteller oder Herkunftsländer eine schwierige Angelegenheit, manchmal -wurden Kodierungsschemata gar nicht unterstützt, was die +wurden Kodierungsschemata gar nicht unterstützt, was die Darstellung des Textes auf diesem Computer unmöglich machte. Für weitere Informationen und technische Details wird der @@ -32,7 +32,7 @@ empfohlen. Unicode hat die meisten dieser Probleme gelöst und wird von jeder modernen Maschine unterstützt. D hat aus den Fehlern -älterer Programmiersprachen gelernt. So sind **alle** Strings +älterer Programmiersprachen gelernt. So sind **alle** Strings in D Unicode-Strings, während sie in Sprachen wie C und C++ als einfache Byte-Arrays implementiert sind. @@ -49,12 +49,12 @@ Der Spezifikation nach führt das Speichern von Nicht-Unicode-Daten in D-Stringtypen zu einem Fehler. Programme scheitern auf verschiedene Arten, wenn ein String nicht korrekt kodiert ist. -Um andere Stringkodierungen nutzen, oder C/C++-Verhalten zu +Um andere Stringkodierungen nutzen, oder C/C++-Verhalten zu erhalten, können `ubyte[]` oder `char*` genutzt werden. ## Strings in Range-Algorithmen -*Die Lektüre der Sektion [Range Algorithmen](gems/range-algorithms) +*Die Lektüre der Sektion [Range Algorithmen](gems/range-algorithms) wird für diesen Abschnitt empfohlen.* Ein paar wichtige Hinweise sind im Umgang mit Unicode in D zu beachten. @@ -71,7 +71,7 @@ Dieses Verhalten hat einige Implikationen. Viele Leute verwirrt, dass `std.traits.hasLength!(string)` zu `False` ausgewertet wird. Hinsichtlich der Range-API liegt dies in der Tatsache begründet, dass die `length`-Methode von `string` die **Anzahl der Elemente in -dem String** und nicht die Anzahl der Elemente, *über die die +dem String** und nicht die Anzahl der Elemente, *über die die Range-Funktion iteriert*, zurückgibt. Anhand dieses Beispiels wird deutlich, warum diese beiden Dinge diff --git a/gems/uniform-function-call-syntax-ufcs.md b/gems/uniform-function-call-syntax-ufcs.md index ebbf258..96e311f 100644 --- a/gems/uniform-function-call-syntax-ufcs.md +++ b/gems/uniform-function-call-syntax-ufcs.md @@ -1,18 +1,18 @@ # Uniform Function Call Syntax (UFCS) -**UFCS** (dt. etwa: einheitliche Funktionsaufruf-Syntax) ist -ein Hauptmerkmal von D, dass Wiederverwendbarkeit und +**UFCS** (dt. etwa: einheitliche Funktionsaufruf-Syntax) ist +ein Hauptmerkmal von D, dass Wiederverwendbarkeit und Skalierbarkeit durch klar definierte Kapselung ermöglicht. UCFS erlaubt es, den Aufruf der freien Funktion `fun(a)` als Memberfunktionsaufruf `a.fun()` zu schreiben. Wenn `a.fun()` vom Compiler gesehen wird, und der Typ von `a` -keine Memberfunktion `fun()` besitzt, wird versucht eine +keine Memberfunktion `fun()` besitzt, wird versucht eine globale Funktion zu finden, deren erster Parameter dem von `a` entspricht. -Dieses Feature ist besonders bei der Verkettung von +Dieses Feature ist besonders bei der Verkettung von Funktionsaufrufen nützlich. foo(bar(a)) @@ -21,8 +21,8 @@ kann dank UFCS auch so geschrieben werden: a.bar().foo() -Ferner können in D bei Funktionen ohne Argumente die runden -Klammern weggelassen werden. Somit kann _jede_ Funktion wie +Ferner können in D bei Funktionen ohne Argumente die runden +Klammern weggelassen werden. Somit kann _jede_ Funktion wie ein Property verwendet werden: import std.uni : toLower; @@ -47,7 +47,7 @@ können. Dies führt zu klarem und wartbarem Code: ```d import std.stdio : writefln, writeln; -import std.algorithm.iteration : filter; +import std.algorithm.iteration : filter; import std.range : iota; void main() @@ -61,6 +61,6 @@ void main() // Traditioneller Stil: writeln(filter!(a => a % 2 == 0) - (iota(10))); + (iota(10))); } ``` diff --git a/gems/unittesting.md b/gems/unittesting.md index a427961..12b9e6a 100644 --- a/gems/unittesting.md +++ b/gems/unittesting.md @@ -1,11 +1,11 @@ # Unittests -Tests sind ein exzellenter Weg der Gewährleistung von Stabilität -und Fehlerfreiheit in der Anwendungsentwicklung. Sie dienen als -interaktive Dokumentation und erlauben Codeveränderungen ohne -Angst davor, bereits bestehende Funktionalität zu zerstören. -D bietet mit `unittest`-Blöcken eine bequeme, native Syntax, die -überall in einem D-Modul eingesetzt werden kann, um +Tests sind ein exzellenter Weg der Gewährleistung von Stabilität +und Fehlerfreiheit in der Anwendungsentwicklung. Sie dienen als +interaktive Dokumentation und erlauben Codeveränderungen ohne +Angst davor, bereits bestehende Funktionalität zu zerstören. +D bietet mit `unittest`-Blöcken eine bequeme, native Syntax, die +überall in einem D-Modul eingesetzt werden kann, um Quellcode-Funktionalität zu prüfen. // Unittest der Funktion myAbs @@ -20,7 +20,7 @@ auf Abruf. ### Ausführung von `unittest`-Blöcken -`unittest`-Blöcke können beliebigen Code enthalten, der +`unittest`-Blöcke können beliebigen Code enthalten, der nur kompiliert und ausgeführt wird, wenn das DMD-Compiler-Flag `-unittest` gesetzt ist. Auch DUB bietet die Kompilierung und Ausführung von Unittests mit dem Befehl `dub test` an. @@ -29,7 +29,7 @@ Ausführung von Unittests mit dem Befehl `dub test` an. Typischerweise enthalten `unittest`s `assert`-Ausdrücke, die die Funktionalität einer gegebenen Funktion sicherstellen. -`unittest`-Blöcke befinden sich in der Regel in der Nähe der +`unittest`-Blöcke befinden sich in der Regel in der Nähe der einer Funktionsdefinition oder sogar in Klassen und Strukturen. ### Erhöhung der Code-Abdeckung @@ -42,9 +42,9 @@ Der DMD-Compiler erlaubt eine einfache Berichterstellung der Code-Abdeckung durch Hinzufügen von `-cov`. Damit wird für jedes Modul eine `.lst`-Datei mit detailierten Statistiken erzeugt. -Da der Compiler Attribute von Template-Quellcode automatisch -ableiten kann, ist es ein gängiges Muster, Unittests mit -Annotationen zu versehen, um die Verwendung gewünschter Attribute +Da der Compiler Attribute von Template-Quellcode automatisch +ableiten kann, ist es ein gängiges Muster, Unittests mit +Annotationen zu versehen, um die Verwendung gewünschter Attribute sicherzustellen: unittest @safe @nogc nothrow pure @@ -105,7 +105,7 @@ auszuführen" */ unittest { Vector3 vec; - // .init ist eine spezielles + // .init ist eine spezielles // Property in D, die den // Initialwert des Typs angibt. assert(vec.x == double.init); diff --git a/welcome/links-documentation.md b/welcome/links-documentation.md index ed15fd7..67258ea 100644 --- a/welcome/links-documentation.md +++ b/welcome/links-documentation.md @@ -12,5 +12,5 @@ weitere Informationen und Hilfe erhalten: ### Hilfe * Stelle Fragen im [IRC-Kanal](https://kiwiirc.com/client/irc.freenode.net/d) auf Freenode (irc://irc.freenode.net/d) -* Bitte um Hilfe im Forum unter [D.learn](http://forum.dlang.org/group/learn) +* Bitte um Hilfe im Forum unter [D.learn](http://forum.dlang.org/group/learn) * Melde einen Fehler auf dem [D-Bugtracker](https://issues.dlang.org) diff --git a/welcome/welcome-to-d.md b/welcome/welcome-to-d.md index e088755..0310e94 100644 --- a/welcome/welcome-to-d.md +++ b/welcome/welcome-to-d.md @@ -56,9 +56,9 @@ import std.range; void main() { writeln("Hallo Welt!"); - + // Ein Beispiel für erfahrenere User: - // Nimm drei Arrays, und sortiere + // Nimm drei Arrays, und sortiere // ohne weitere Speicherallokation // über alle Arrays in-place int[] arr1 = [4, 9, 7]; @@ -66,7 +66,7 @@ void main() int[] arr3 = [6, 8, 3]; sort(chain(arr1, arr2, arr3)); writefln("%s\n%s\n%s\n", arr1, arr2, arr3); - // Mehr über dieses Beispiel unter + // Mehr über dieses Beispiel unter // dem Stichwort "Ranges" } ```