update
This commit is contained in:
parent
dc0a64baa8
commit
d15f34a530
1 changed files with 426 additions and 26 deletions
|
@ -24,22 +24,27 @@ echo "~/.dlang/dmd-2.079.0/activate" >> ~/.profile # Add dmd and dub to PATH on
|
||||||
|
|
||||||
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd.2.079.0.dmg.
|
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd.2.079.0.dmg.
|
||||||
* Open `dmd.2.079.0.dmg`
|
* Open `dmd.2.079.0.dmg`
|
||||||
* Run `DMD2.pkg` (you might need to activate the “allow installing applications from unverified developers” option in your security settings) and install with the default settings.
|
* Run `DMD2.pkg` (you might need to activate the “allow installing applications
|
||||||
|
from unverified developers” option in your security settings) and install with
|
||||||
|
the default settings.
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd-2.079.0.exe.
|
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd-2.079.0.exe.
|
||||||
* Run `dmd-2.079.0.exe` and install with the default settings (this will also install Visual Studio if you do not have it installed yet).
|
* Run `dmd-2.079.0.exe` and install with the default settings (this will also
|
||||||
|
install Visual Studio if you do not have it installed yet).
|
||||||
|
|
||||||
### Recommended editor setup
|
### Recommended editor setup
|
||||||
|
|
||||||
Visual Studio Code is the recommended editor, because it has the best D integration at the moment.
|
Visual Studio Code is the recommended editor, because it has the best D
|
||||||
If you want to use another editor or IDE, that is perfectly fine.
|
integration at the moment. If you want to use another editor or IDE, that is
|
||||||
However, instructions will only be provided for Visual Studio Code.
|
perfectly fine. However, instructions will only be provided for Visual Studio
|
||||||
|
Code.
|
||||||
|
|
||||||
#### Installation of Visual Studio Code
|
#### Installation of Visual Studio Code
|
||||||
|
|
||||||
Download and install Visual Studio Code from here: https://code.visualstudio.com/. OS X users can also install it using Homebrew:
|
Download and install Visual Studio Code from here:
|
||||||
|
https://code.visualstudio.com/. OS X users can also install it using Homebrew:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew tap caskroom/cask
|
brew tap caskroom/cask
|
||||||
|
@ -53,7 +58,8 @@ brew cask install visual-studio-code
|
||||||
|----------------|---------|
|
|----------------|---------|
|
||||||
|OS X |⌘ + ⇧ + X|
|
|OS X |⌘ + ⇧ + X|
|
||||||
|Windows |⌃ + ⇧ + X|
|
|Windows |⌃ + ⇧ + X|
|
||||||
* Install the extension “D Programming Language (code-d)” (requires that git is installed).
|
* Install the extension “D Programming Language (code-d)” (requires that git is
|
||||||
|
installed).
|
||||||
* Restart Visual Studio Code.
|
* Restart Visual Studio Code.
|
||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
|
@ -71,9 +77,10 @@ void main() {
|
||||||
### Imports and modules
|
### Imports and modules
|
||||||
|
|
||||||
D has the concept of _modules_ and _packages_.
|
D has the concept of _modules_ and _packages_.
|
||||||
By importing a certain module with the `import` statement, all public symbols from module become available.
|
By importing a certain module with the `import` statement, all public symbols
|
||||||
The standard library, called Phobos, is located in the `std` package.
|
from module become available. The standard library, called Phobos, is located
|
||||||
E.g. in order to import the `file` module from Phobos, you would write:
|
in the `std` package. E.g. in order to import the `file` module from Phobos, you
|
||||||
|
would write:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
import std.file;
|
import std.file;
|
||||||
|
@ -81,7 +88,8 @@ import std.file;
|
||||||
|
|
||||||
#### Selective imports
|
#### Selective imports
|
||||||
|
|
||||||
It is possible (and often good style) to import symbols selectively from a module:
|
It is possible (and often good style) to import symbols selectively from a
|
||||||
|
module:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
import std.stdio: writeln, writefln;
|
import std.stdio: writeln, writefln;
|
||||||
|
@ -91,8 +99,9 @@ import std.stdio: writeln, writefln;
|
||||||
|
|
||||||
It is not necessary to place imports at the beginning of a file.
|
It is not necessary to place imports at the beginning of a file.
|
||||||
They can be located anywhere in the code.
|
They can be located anywhere in the code.
|
||||||
If they appear inside a certain scope (delimited by braces), the imported symbols are only available inside that scope.
|
If they appear inside a certain scope (delimited by braces), the imported
|
||||||
Here is an alternative version of the hello world program:
|
symbols are only available inside that scope. Here is an alternative version of
|
||||||
|
the hello world program:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
void main()
|
void main()
|
||||||
|
@ -114,18 +123,20 @@ D has the following basic types:
|
||||||
|
|
||||||
| Datatypes | Size |
|
| Datatypes | Size |
|
||||||
| ------------------------------- | ------------------------------------------------------------ |
|
| ------------------------------- | ------------------------------------------------------------ |
|
||||||
| `bool` `byte`, `ubyte`, `char` | 8-bit |
|
| `bool`, `byte`, `ubyte`, `char` | 8-bit |
|
||||||
| `short`, `ushort`, `wchar` | 16-bit |
|
| `short`, `ushort`, `wchar` | 16-bit |
|
||||||
| `int`, `uint`, `dchar`, `float` | 32-bit |
|
| `int`, `uint`, `dchar`, `float` | 32-bit |
|
||||||
| `long`, `ulong`, `double` | 64-bit |
|
| `long`, `ulong`, `double` | 64-bit |
|
||||||
| `real` | >= 64-bit (generally 64-bit, but 80-bit on Intel x86 32-bit) |
|
| `real` | >= 64-bit (generally 64-bit, but 80-bit on Intel x86 32-bit) |
|
||||||
|
|
||||||
`char` represents UTF-8 characters, `wchar`represents UTF-16 characters, and `dchar` represents UTF-32 characters.
|
`char` represents UTF-8 characters, `wchar`represents UTF-16 characters, and
|
||||||
|
`dchar` represents UTF-32 characters.
|
||||||
|
|
||||||
#### Type conversion
|
#### Type conversion
|
||||||
|
|
||||||
For integer types, automatic type conversion is only allowed if no precision is lost (e.g. `int` to `long`).
|
For integer types, automatic type conversion is only allowed if no precision is
|
||||||
All conversion between floating point types are allowed (e.g. `double` to `float`).
|
lost (e.g. `int` to `long`). All conversion between floating point types are
|
||||||
|
allowed (e.g. `double` to `float`).
|
||||||
|
|
||||||
Manual type conversion is achieved with the `cast` expression:
|
Manual type conversion is achieved with the `cast` expression:
|
||||||
|
|
||||||
|
@ -136,8 +147,9 @@ int b = cast(int) a;
|
||||||
|
|
||||||
#### Type properties
|
#### Type properties
|
||||||
|
|
||||||
All types have a property `.init` to which variables of that type are initialized, if they are not initialized explicitly.
|
All types have a property `.init` to which variables of that type are
|
||||||
For integer types, this is `0` and for floating point types it is `nan`.
|
initialized, if they are not initialized explicitly. For integer types, this is
|
||||||
|
`0` and for floating point types it is `nan`.
|
||||||
|
|
||||||
Every type also has a `.stringof` property which yields its name as a string.
|
Every type also has a `.stringof` property which yields its name as a string.
|
||||||
|
|
||||||
|
@ -162,7 +174,8 @@ And so do floating point types:
|
||||||
|
|
||||||
#### Indexing
|
#### Indexing
|
||||||
|
|
||||||
For indexing, usually the alias type `size_t` is used, which is large enough to represent an offset into all addressable memory.
|
For indexing, usually the alias type `size_t` is used, which is large enough to
|
||||||
|
represent an offset into all addressable memory.
|
||||||
|
|
||||||
### Variable declarations
|
### Variable declarations
|
||||||
|
|
||||||
|
@ -184,8 +197,8 @@ It is also possible to declare several variables at once:
|
||||||
int myVar, someOtherVar;
|
int myVar, someOtherVar;
|
||||||
```
|
```
|
||||||
|
|
||||||
D has automatic type deduction, so when explicitly initializing a variable, it is not necessary to mention the type.
|
D has automatic type deduction, so when explicitly initializing a variable, it
|
||||||
Instead we can use the `auto` keyword:
|
is not necessary to mention the type. Instead we can use the `auto` keyword:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
auto myVar = 42;
|
auto myVar = 42;
|
||||||
|
@ -197,6 +210,44 @@ Here is a combination of the above notations:
|
||||||
auto myInt = 42, myFloat = 4.2f;
|
auto myInt = 42, myFloat = 4.2f;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Mutability
|
||||||
|
|
||||||
|
Objects in D are mutable by default, but is possible to change this using
|
||||||
|
type qualifiers:
|
||||||
|
|
||||||
|
#### `immutable`
|
||||||
|
|
||||||
|
An object declared as `immutable` is enforced by the compiler to never change its
|
||||||
|
value.
|
||||||
|
|
||||||
|
```D
|
||||||
|
immutable int a;
|
||||||
|
a = 5; // error
|
||||||
|
```
|
||||||
|
|
||||||
|
`immutable` objects are implicitly shared accross threads, because the can never
|
||||||
|
change their value and thus race conditions are impossible.
|
||||||
|
|
||||||
|
#### `const`
|
||||||
|
|
||||||
|
`const` objects also can not be modified, but this is enforced only in the
|
||||||
|
current scope. This means, that the object could be modified from a different
|
||||||
|
scope. Both mutable and `immutable` objects implictly convert to `const`
|
||||||
|
objects:
|
||||||
|
|
||||||
|
```D
|
||||||
|
void foo(const char[] s)
|
||||||
|
{
|
||||||
|
// Do something with s
|
||||||
|
}
|
||||||
|
// Both calls are valid, thanks to const
|
||||||
|
foo("abcd"); // a string is an immutable array of char
|
||||||
|
foo("abcd".dup); // dup creates a mutable copy
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `immutable` and `const` are transitive, i.e. the apply recursively to all
|
||||||
|
subcomponents of a type they are applied to.
|
||||||
|
|
||||||
### Functions
|
### Functions
|
||||||
|
|
||||||
The basic syntax for functions is very similar to C:
|
The basic syntax for functions is very similar to C:
|
||||||
|
@ -254,6 +305,219 @@ void fun() {
|
||||||
static assert(!__traits(compiles, fun_secret())); // fun_secret is not visible here
|
static assert(!__traits(compiles, fun_secret())); // fun_secret is not visible here
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Memory and pointers
|
||||||
|
|
||||||
|
D uses a garbage collector by default, but is also possible to do manual memory
|
||||||
|
management if needed.
|
||||||
|
|
||||||
|
D provides pointer types `T*` like in C:
|
||||||
|
|
||||||
|
```D
|
||||||
|
int a;
|
||||||
|
int* b = &a; // b contains address of a
|
||||||
|
auto c = &a; // c is int* and contains address of a
|
||||||
|
```
|
||||||
|
|
||||||
|
To allocate a new memory block on the garbage collected heap, use the `new`
|
||||||
|
operator:
|
||||||
|
|
||||||
|
```D
|
||||||
|
int* a = new int;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Memory safety
|
||||||
|
|
||||||
|
In general, pointer arithmetic like in C is allowed. This results in the usual
|
||||||
|
safety issues. To counter this, D defines 3 safety levels for functions:
|
||||||
|
`@safe`, `@trusted`, and `@system`. The default is `@system`, which gives no
|
||||||
|
safety guarantees. Functions annotated with `@safe` are only allowed to call
|
||||||
|
other `@safe` and `@trusted` functions and it is not possible to do pointer
|
||||||
|
arithmetic in them:
|
||||||
|
|
||||||
|
```D
|
||||||
|
void main() @safe {
|
||||||
|
int a = 5;
|
||||||
|
int* p = &a;
|
||||||
|
int* c = p + 5; // error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`@trusted` functions are functions that are manually verified to provide an
|
||||||
|
`@safe` interface. They create a bridge between `@safe` code and dirty low-level
|
||||||
|
code. Only use them very carefully!
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
One way to create custom data types in D is with `struct`s:
|
||||||
|
|
||||||
|
```D
|
||||||
|
struct Person {
|
||||||
|
int age;
|
||||||
|
int height;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unless created with the `new` operator, `sturct`s are always constructed on the
|
||||||
|
stack and copied _by value_ in assignments and as parameters to function calls.
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto p = Person(30, 180);
|
||||||
|
auto t = p; // copy
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also define a custom constructor:
|
||||||
|
|
||||||
|
```D
|
||||||
|
struct Person {
|
||||||
|
int age;
|
||||||
|
int height;
|
||||||
|
this(int age, int height) {
|
||||||
|
this.age = age;
|
||||||
|
this. height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Member functions
|
||||||
|
|
||||||
|
`struct`s can have member functions. By default, they are `public` and
|
||||||
|
accessible from outside. By marking them `private`, you can limit access to
|
||||||
|
functions in the same module (different from C++ / Java etc.!):
|
||||||
|
|
||||||
|
```D
|
||||||
|
struct Person {
|
||||||
|
void doStuff() {
|
||||||
|
/* … */
|
||||||
|
}
|
||||||
|
private void privateStuff() {
|
||||||
|
/* … */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto p = Person();
|
||||||
|
p.doStuff(); // call method doStuff
|
||||||
|
p.privateStuff(); // forbidden
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `const` member functions
|
||||||
|
|
||||||
|
Member functions declared const can not modify any members. They can be called
|
||||||
|
on `immutable` and `const` objects.
|
||||||
|
|
||||||
|
#### `static` member functions
|
||||||
|
|
||||||
|
They work basically the same as in C etc.
|
||||||
|
|
||||||
|
### Arrays
|
||||||
|
|
||||||
|
D has two types of arrays, static arrays and dynamic arrays. Both of them are
|
||||||
|
bounds checked unless this feature is explicitly switched of with the compiler
|
||||||
|
flag `--boundcheck=off`.
|
||||||
|
|
||||||
|
#### Static arrays
|
||||||
|
|
||||||
|
Static arrays are stored on he stack or in static memory, depending on where
|
||||||
|
they are defined. They have a fixed, compile-time known length. The length is
|
||||||
|
part of the type:
|
||||||
|
|
||||||
|
```D
|
||||||
|
int[8] arr;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Dynamic arrays
|
||||||
|
|
||||||
|
Dynamic arrays are stored on the heap and have a variabe length, which can
|
||||||
|
change during runtime. A dynamic array is created with the new expression:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto size = 8;
|
||||||
|
int[] arr = new int[size];
|
||||||
|
```
|
||||||
|
|
||||||
|
The type `int[]` is called a _slice_ of `int`. Slices will be explained in more
|
||||||
|
detail in the next section.
|
||||||
|
|
||||||
|
Creating multidimensional arrays is also easy:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto matrix = new int[3][3];
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Array operations and properties
|
||||||
|
|
||||||
|
Array concatenation is done with the `~` operator, which creates a new dynamic
|
||||||
|
array.
|
||||||
|
|
||||||
|
Mathematical operations can be applied to whole arrays using a syntax like
|
||||||
|
`c[] = a[] + b[]`, for example. This adds all elements of `a` and `b` so that
|
||||||
|
`c[0] = a[0] + b[0]`, `c[1] = a[1] + b[1]`, etc. It is also possible to perform
|
||||||
|
operations on a whole array with a single value:
|
||||||
|
|
||||||
|
```D
|
||||||
|
a[] *= 2; // multiple all elements by 2
|
||||||
|
a[] %= 26; // calculate the modulo by 26 for all a's
|
||||||
|
```
|
||||||
|
|
||||||
|
These operations can be optimized by the compiler using _SIMD_ instructions.
|
||||||
|
|
||||||
|
Both static and dynamic arrays provide the property `.length`, which is
|
||||||
|
read-only for static arrays, but can be used in the case of dynamic arrays to
|
||||||
|
change its size dynamically. The property .dup creates a copy of the array.
|
||||||
|
|
||||||
|
When indexing an array through the `arr[idx]` syntax, a special `$` symbol
|
||||||
|
denotes an array's length. For example, `arr[$ - 1]` references the last element
|
||||||
|
and is a short form for `arr[arr.length - 1]`.
|
||||||
|
|
||||||
|
### Slices
|
||||||
|
|
||||||
|
Slices are object of the type `T[]`for any given type `T. Slices provide a
|
||||||
|
_view_ to a subset of an array.
|
||||||
|
|
||||||
|
A slice consists basically of two members:
|
||||||
|
|
||||||
|
```D
|
||||||
|
T* ptr;
|
||||||
|
size_t length;
|
||||||
|
```
|
||||||
|
|
||||||
|
As we have already seen in the previous section, we can get slices by
|
||||||
|
allocating a new dynamic array:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto arr = new int[5];
|
||||||
|
assert(arr.length == 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
The slice does not own the memory, it is managed by the garbage collector. The
|
||||||
|
slice is just a view on the memory.
|
||||||
|
|
||||||
|
You can also get slices to already existing memory:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto arr = new int[5];
|
||||||
|
auto newArr = arr;
|
||||||
|
auto smallerViewArr = arr[1 .. 4]; // index 4 is not included
|
||||||
|
assert(smallerViewArr.length == 3);
|
||||||
|
assert(newArr.length == 5);
|
||||||
|
smallerViewArr[0] = 10;
|
||||||
|
assert(newArr[1] == 10 && arr[1] == 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, it is important to keep in mind, that this is only a view to memory. No
|
||||||
|
memory is copied.
|
||||||
|
|
||||||
|
### Alias and `string`s
|
||||||
|
|
||||||
|
By using the `alias` statement , we can create new “names” for existing types:
|
||||||
|
|
||||||
|
```D
|
||||||
|
alias string = immutable(char)[];
|
||||||
|
```
|
||||||
|
|
||||||
|
This works very similar to `typedef` from C / C++.
|
||||||
|
|
||||||
|
The above definition of `string` is atually the definition that is used by D.
|
||||||
|
This means that `string`s are just mutable slices of `immutable` `char`s.
|
||||||
|
|
||||||
### Control flow
|
### Control flow
|
||||||
|
|
||||||
#### if…else
|
#### if…else
|
||||||
|
@ -272,7 +536,8 @@ if (a == 5) {
|
||||||
|
|
||||||
#### switch…case
|
#### switch…case
|
||||||
|
|
||||||
Also very similar to how it is defined in other languages, but for it works for integer types, bools and strings (which will be covered later).
|
Also very similar to how it is defined in other languages, but for it works for
|
||||||
|
integer types, bools and strings (which will be covered later).
|
||||||
|
|
||||||
```D
|
```D
|
||||||
string myString;
|
string myString;
|
||||||
|
@ -304,13 +569,15 @@ switch(c) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Loops
|
#### Old fashioned loops
|
||||||
|
|
||||||
`while`-, `do`…`while`- and classical `for`-loops all work the same as in C++/Java etc.
|
`while`-, `do`…`while`- and classical `for`-loops all work the same as in C++ /
|
||||||
|
Java etc.
|
||||||
|
|
||||||
##### Breaking out of outer loops
|
##### Breaking out of outer loops
|
||||||
|
|
||||||
As usual, you can break out of a loop immediately by using the `break` keyword. Additionally, you can also break out of outer loops by using labels:
|
As usual, you can break out of a loop immediately by using the `break` keyword.
|
||||||
|
Additionally, you can also break out of outer loops by using labels:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
outer:
|
outer:
|
||||||
|
@ -321,3 +588,136 @@ for (int i = 0; i < 10; ++i) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `foreach` loops
|
||||||
|
|
||||||
|
D has a `foreach` loops which allows for much better readable iterations.
|
||||||
|
|
||||||
|
##### Element iteration
|
||||||
|
|
||||||
|
We can easily iterate ofer slices using `foreach`:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto arr = new int[5];
|
||||||
|
foreach (e; arr) {
|
||||||
|
writeln(e);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Access by reference
|
||||||
|
|
||||||
|
By default the elements are copied during the iteration. If we want _in-place_
|
||||||
|
modification, we can use the `ref` qualifier:
|
||||||
|
|
||||||
|
```D
|
||||||
|
auto arr = new int[5];
|
||||||
|
foreach (ref e; arr) {
|
||||||
|
e = 5;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Iterate `n` times
|
||||||
|
|
||||||
|
It is easy to write iterations, which should be executed `n` times by using the
|
||||||
|
`..` syntax:
|
||||||
|
|
||||||
|
```D
|
||||||
|
foreach (i; 0 .. 3) {
|
||||||
|
writeln(i);
|
||||||
|
}
|
||||||
|
// prints 0 1 2
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Iteration with index counter
|
||||||
|
|
||||||
|
For slices, it's also possible to access a separate index variable:
|
||||||
|
|
||||||
|
```D
|
||||||
|
foreach (i, e; [4, 5, 6]) {
|
||||||
|
writeln(i, ":", e);
|
||||||
|
}
|
||||||
|
// prints 0:4 1:5 2:6
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ranges
|
||||||
|
|
||||||
|
Ranges are a very important concept for iteration in D. We can use `foreach`
|
||||||
|
loops, to iterate over ranges:
|
||||||
|
|
||||||
|
```D
|
||||||
|
foreach (element; range) {
|
||||||
|
// Loop body
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we use `foreach` with a range, this gets lowered to the compiler to something
|
||||||
|
similar to this:
|
||||||
|
|
||||||
|
```D
|
||||||
|
for (auto __rangeCopy = range; !__rangeCopy.empty; __rangeCopy.popFront()) {
|
||||||
|
auto element = __rangeCopy.front;
|
||||||
|
// Loop body...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This leads us to what ranges (or more specific `InputRange`s) actually are:
|
||||||
|
Anything, that implements the member functions needed by the above lowering:
|
||||||
|
|
||||||
|
```D
|
||||||
|
interface InputRange(E) {
|
||||||
|
bool empty() @property;
|
||||||
|
E front() @property;
|
||||||
|
void popFront();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, ranges do not need to _implement_ such an interface in terms of
|
||||||
|
inheritance, they just have to provide the above member functions.
|
||||||
|
Typically, ranges are implemented as `struct`s (because most of the time, ranges
|
||||||
|
should be value types), but is also possible to implement them as `class`es,
|
||||||
|
which will be introduced later.
|
||||||
|
|
||||||
|
#### Laziness
|
||||||
|
|
||||||
|
Ranges are _lazy_. They won't be evaluated until requested. Hence, a range from
|
||||||
|
an infinite range can be taken:
|
||||||
|
|
||||||
|
```D
|
||||||
|
42.repeat.take(3).writeln; // [42, 42, 42]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Copying ranges
|
||||||
|
|
||||||
|
Copying a range by just using the assignment operator might not have the desired
|
||||||
|
effect, because iterationg over a range can be destructive (i.e. when the range
|
||||||
|
holds internal pointers and a deep copy would be necessary). “copyable” ranges
|
||||||
|
are called `ForwardRange`s. They need to implement a `.save` method which
|
||||||
|
returns a copy of the range:
|
||||||
|
|
||||||
|
```D
|
||||||
|
interface ForwardRange(E) : InputRange!E
|
||||||
|
{
|
||||||
|
typeof(this) save();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `RandomAccessRange`s
|
||||||
|
|
||||||
|
A `RandomAccessRange` is a `ForwardRange` which has a know `length` for which
|
||||||
|
each element can be access directly:
|
||||||
|
|
||||||
|
```D
|
||||||
|
interface RandomAccessRange(E) : ForwardRange!E
|
||||||
|
{
|
||||||
|
E opIndex(size_t i); // can access elements using range[i] syntax
|
||||||
|
size_t length() @property;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Slices are the most prominent example of `RandomAccessRange`s in
|
||||||
|
|
||||||
|
#### Lazy range algorithms
|
||||||
|
|
||||||
|
The D standard library provides a huge arsenal of lazy range algorithm
|
||||||
|
functions. Most of them can be found in in the `std.range` and `std.algorithm`
|
||||||
|
packages.
|
||||||
|
|
Loading…
Reference in a new issue