add sections about compiling, range algorithms and unittesting
This commit is contained in:
parent
e1a4ec035e
commit
9b06a68383
1 changed files with 276 additions and 91 deletions
|
@ -1,81 +1,89 @@
|
||||||
# Hands-On DLang
|
# Hands-On DLang
|
||||||
|
|
||||||
* [Hands-On DLang](#hands-on-dlang)
|
- [Hands-On DLang](#hands-on-dlang)
|
||||||
* [Setup](#setup)
|
- [Setup](#setup)
|
||||||
* [Installing DMD and DUB](#installing-dmd-and-dub)
|
- [Installing DMD and DUB](#installing-dmd-and-dub)
|
||||||
* [OS X](#os-x)
|
- [OS X](#os-x)
|
||||||
* [Installing with Homebrew (recommended)](#installing-with-homebrew-recommended)
|
- [Installing with Homebrew (recommended)](#installing-with-homebrew-recommended)
|
||||||
* [Installing locally using the install script](#installing-locally-using-the-install-script)
|
- [Installing locally using the install script](#installing-locally-using-the-install-script)
|
||||||
* [Installing using the installer](#installing-using-the-installer)
|
- [Installing using the installer](#installing-using-the-installer)
|
||||||
* [Windows](#windows)
|
- [Windows](#windows)
|
||||||
* [Recommended editor setup](#recommended-editor-setup)
|
- [Recommended editor setup](#recommended-editor-setup)
|
||||||
* [Installation of Visual Studio Code](#installation-of-visual-studio-code)
|
- [Installation of Visual Studio Code](#installation-of-visual-studio-code)
|
||||||
* [Extension setup](#extension-setup)
|
- [Extension setup](#extension-setup)
|
||||||
* [Basics](#basics)
|
- [Project setup and compiling](#project-setup-and-compiling)
|
||||||
* [Hello World](#hello-world)
|
- [Without DUB](#without-dub)
|
||||||
* [Imports and modules](#imports-and-modules)G
|
- [With DUB](#with-dub)
|
||||||
* [Selective imports](#selective-imports)
|
- [Basics](#basics)
|
||||||
* [Scoped imports](#scoped-imports)
|
- [Hello World](#hello-world)
|
||||||
* [Imports match files and directories](#imports-match-files-and-directories)
|
- [Imports and modules](#imports-and-modules)
|
||||||
* [Basic Types](#basic-types)
|
- [Selective imports](#selective-imports)
|
||||||
* [Type conversion](#type-conversion)
|
- [Scoped imports](#scoped-imports)
|
||||||
* [Type properties](#type-properties)
|
- [Imports match files and directories](#imports-match-files-and-directories)
|
||||||
* [Indexing](#indexing)
|
- [Basic Types](#basic-types)
|
||||||
* [Variable declarations](#variable-declarations)
|
- [Type conversion](#type-conversion)
|
||||||
* [Mutability](#mutability)
|
- [Type properties](#type-properties)
|
||||||
* [`immutable`](#immutable)
|
- [Indexing](#indexing)
|
||||||
* [`const`](#const)
|
- [Variable declarations](#variable-declarations)
|
||||||
* [Functions](#functions)
|
- [Mutability](#mutability)
|
||||||
* [Return type deduction](#return-type-deduction)
|
- [`immutable`](#immutable)
|
||||||
* [Default arguments](#default-arguments)
|
- [`const`](#const)
|
||||||
* [Local functions](#local-functions)
|
- [Functions](#functions)
|
||||||
* [Memory and pointers](#memory-and-pointers)
|
- [Return type deduction](#return-type-deduction)
|
||||||
* [Memory safety](#memory-safety)
|
- [Default arguments](#default-arguments)
|
||||||
* [Structs](#structs)
|
- [Local functions](#local-functions)
|
||||||
* [Member functions](#member-functions)
|
- [Memory and pointers](#memory-and-pointers)
|
||||||
* [`const` member functions](#const-member-functions)
|
- [Memory safety](#memory-safety)
|
||||||
* [`static` member functions](#static-member-functions)
|
- [Structs](#structs)
|
||||||
* [Arrays](#arrays)
|
- [Member functions](#member-functions)
|
||||||
* [Static arrays](#static-arrays)
|
- [`const` member functions](#const-member-functions)
|
||||||
* [Dynamic arrays](#dynamic-arrays)
|
- [`static` member functions](#static-member-functions)
|
||||||
* [Array operations and properties](#array-operations-and-properties)
|
- [Arrays](#arrays)
|
||||||
* [Slices](#slices)
|
- [Static arrays](#static-arrays)
|
||||||
* [Alias and `string`s](#alias-and-strings)
|
- [Dynamic arrays](#dynamic-arrays)
|
||||||
* [Control flow](#control-flow)
|
- [Array operations and properties](#array-operations-and-properties)
|
||||||
* [if…else](#if%E2%80%A6else)
|
- [Slices](#slices)
|
||||||
* [switch…case](#switch%E2%80%A6case)
|
- [Alias and `string`s](#alias-and-strings)
|
||||||
* [Old fashioned loops](#old-fashioned-loops)
|
- [Control flow](#control-flow)
|
||||||
* [Breaking out of outer loops](#breaking-out-of-outer-loops)
|
- [if…else](#ifelse)
|
||||||
* [`foreach` loops](#foreach-loops)
|
- [switch…case](#switchcase)
|
||||||
* [Element iteration](#element-iteration)
|
- [Old fashioned loops](#old-fashioned-loops)
|
||||||
* [Access by reference](#access-by-reference)
|
- [Breaking out of outer loops](#breaking-out-of-outer-loops)
|
||||||
* [Iterate `n` times](#iterate-n-times)
|
- [`foreach` loops](#foreach-loops)
|
||||||
* [Iteration with index counter](#iteration-with-index-counter)
|
- [Element iteration](#element-iteration)
|
||||||
* [Ranges](#ranges)
|
- [Access by reference](#access-by-reference)
|
||||||
* [Laziness](#laziness)
|
- [Iterate `n` times](#iterate-n-times)
|
||||||
* [Copying ranges](#copying-ranges)
|
- [Iteration with index counter](#iteration-with-index-counter)
|
||||||
* [`RandomAccessRange`s](#randomaccessranges)
|
- [Ranges](#ranges)
|
||||||
* [Lazy range algorithms](#lazy-range-algorithms)
|
- [Laziness](#laziness)
|
||||||
* [Associative arrays](#associative-arrays)
|
- [Copying ranges](#copying-ranges)
|
||||||
* [Classes](#classes)
|
- [`RandomAccessRange`s](#randomaccessranges)
|
||||||
* [Inheritance](#inheritance)
|
- [Lazy range algorithms](#lazy-range-algorithms)
|
||||||
* [Final and abstract member functions](#final-and-abstract-member-functions)
|
- [Associative arrays](#associative-arrays)
|
||||||
* [Checking for identity](#checking-for-identity)
|
- [Classes](#classes)
|
||||||
* [Interfaces](#interfaces)
|
- [Inheritance](#inheritance)
|
||||||
* [Templates](#templates)
|
- [Final and abstract member functions](#final-and-abstract-member-functions)
|
||||||
* [Template functions](#template-functions)
|
- [Checking for identity](#checking-for-identity)
|
||||||
* [Other templates](#other-templates)
|
- [Interfaces](#interfaces)
|
||||||
* [Template value parameters](#template-value-parameters)
|
- [Templates](#templates)
|
||||||
* [Other template parameters](#other-template-parameters)
|
- [Template functions](#template-functions)
|
||||||
* [Delegates](#delegates)
|
- [Other templates](#other-templates)
|
||||||
* [Functions as arguments](#functions-as-arguments)
|
- [Template value parameters](#template-value-parameters)
|
||||||
* [Local functions with context](#local-functions-with-context)
|
- [Other template parameters](#other-template-parameters)
|
||||||
* [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
|
- [Delegates](#delegates)
|
||||||
* [Exceptions](#exceptions)
|
- [Functions as arguments](#functions-as-arguments)
|
||||||
* [`nothrow`](#nothrow)
|
- [Local functions with context](#local-functions-with-context)
|
||||||
* [Gems](#gems)
|
- [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
|
||||||
* [Uniform function call syntax (UFCS)](#uniform-function-call-syntax-ufcs)
|
- [Exceptions](#exceptions)
|
||||||
* [Scope guards](#scope-guards)
|
- [`nothrow`](#nothrow)
|
||||||
|
- [Gems](#gems)
|
||||||
|
- [Uniform function call syntax (UFCS)](#uniform-function-call-syntax-ufcs)
|
||||||
|
- [Scope guards](#scope-guards)
|
||||||
|
- [Range algorithms](#range-algorithms)
|
||||||
|
- [Unittesting](#unittesting)
|
||||||
|
- [Example](#example)
|
||||||
|
- [Executing unittests](#executing-unittests)
|
||||||
|
- [Code coverage](#code-coverage)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -94,21 +102,21 @@ brew install dub
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsS https://dlang.org/install.sh | bash -s dmd
|
curl -fsS https://dlang.org/install.sh | bash -s dmd
|
||||||
echo "~/.dlang/dmd-2.079.0/activate" >> ~/.profile # Add dmd and dub to PATH on starting a bash shell
|
echo "~/.dlang/dmd-2.080.0/activate" >> ~/.profile # Add dmd and dub to PATH on starting a bash shell
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Installing using the installer
|
##### Installing using the installer
|
||||||
|
|
||||||
* 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.080.0/dmd.2.080.0.dmg.
|
||||||
* Open `dmd.2.079.0.dmg`
|
* Open `dmd.2.080.0.dmg`
|
||||||
* Run `DMD2.pkg` (you might need to activate the “allow installing applications
|
* Run `DMD2.pkg` (you might need to activate the “allow installing applications
|
||||||
from unverified developers” option in your security settings) and install with
|
from unverified developers” option in your security settings) and install with
|
||||||
the default settings.
|
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.080.0/dmd-2.080.0.exe.
|
||||||
* Run `dmd-2.079.0.exe` and install with the default settings (this will also
|
* Run `dmd-2.080.0.exe` and install with the default settings (this will also
|
||||||
install Visual Studio if you do not have it installed yet).
|
install Visual Studio if you do not have it installed yet).
|
||||||
|
|
||||||
### Recommended editor setup
|
### Recommended editor setup
|
||||||
|
@ -131,14 +139,58 @@ brew cask install visual-studio-code
|
||||||
#### Extension setup
|
#### Extension setup
|
||||||
|
|
||||||
* Open the Extension view in the sidebar:
|
* Open the Extension view in the sidebar:
|
||||||
|Operating system|Shortcut |
|
| Operating system | Shortcut |
|
||||||
|----------------|---------|
|
| ---------------- | --------- |
|
||||||
|OS X|⌘ + ⇧ + X|
|
| OS X | ⌘ + ⇧ + X |
|
||||||
|Windows|⌃ + ⇧ + X|
|
| Windows | ⌃ + ⇧ + X |
|
||||||
* Install the extension “D Programming Language (code-d)” (requires that git is
|
* Install the extension “D Programming Language (code-d)” (requires that git is
|
||||||
installed).
|
installed).
|
||||||
* Restart Visual Studio Code.
|
* Restart Visual Studio Code.
|
||||||
|
|
||||||
|
### Project setup and compiling
|
||||||
|
|
||||||
|
#### Without DUB
|
||||||
|
|
||||||
|
No special project setup is needed, you can order your sourcefiles any way you
|
||||||
|
like. You can compile them by invoking DMD:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dmd path/to/sourcefile1.d path/to/sourcefile2.d …
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates a binary with the same name as the first sourcefile provided.
|
||||||
|
|
||||||
|
#### With DUB
|
||||||
|
|
||||||
|
To init a DUB project, just run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dub init
|
||||||
|
```
|
||||||
|
|
||||||
|
inside your projects root folder. This runs an interactive dialog which allows
|
||||||
|
you to specify some settings for the project (the project name etc.). Depending
|
||||||
|
on your choice, these settings are saved in a file called either `dub.json` or
|
||||||
|
`dub.sdl`.
|
||||||
|
|
||||||
|
By default, the source files will be contained in the `source` subdirectory.
|
||||||
|
|
||||||
|
To compile the project, simply run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dub build
|
||||||
|
```
|
||||||
|
|
||||||
|
in the project’s root directory. This generates a binary with the same name as
|
||||||
|
the configured project name. It is also possible to compile and run in one step,
|
||||||
|
just run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dub run
|
||||||
|
```
|
||||||
|
|
||||||
|
in the project’s root directory.
|
||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
|
|
||||||
### Hello World
|
### Hello World
|
||||||
|
@ -1034,8 +1086,8 @@ doSomething(&add);
|
||||||
|
|
||||||
#### Local functions with context
|
#### Local functions with context
|
||||||
|
|
||||||
To reference member functions or local functions, `delegate`s have to be used. You
|
To reference member functions or local functions, `delegate`s have to be used.
|
||||||
can create _closures_ with this:
|
You can create _closures_ with this:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
auto foo() {
|
auto foo() {
|
||||||
|
@ -1135,7 +1187,8 @@ one can write
|
||||||
a.bar().foo();
|
a.bar().foo();
|
||||||
```
|
```
|
||||||
|
|
||||||
For functions that take no arguments, it is not necessary to use parenthesis, so any function like that can be used like a property:
|
For functions that take no arguments, it is not necessary to use parenthesis,
|
||||||
|
so any function like that can be used like a property:
|
||||||
|
|
||||||
```D
|
```D
|
||||||
import std.uni: toLower;
|
import std.uni: toLower;
|
||||||
|
@ -1143,7 +1196,8 @@ auto a = "Cool Stuff".toLower;
|
||||||
assert(a == "cool stuff");
|
assert(a == "cool stuff");
|
||||||
```
|
```
|
||||||
|
|
||||||
UFCS is especially important when dealing with ranges where several algorithms can be put together to perform complex operations, still allowing to write clear and manageable code.
|
UFCS is especially important when dealing with ranges where several algorithms
|
||||||
|
can be put together to perform complex operations, still allowing to write clear and manageable code.
|
||||||
|
|
||||||
```D
|
```D
|
||||||
import std.algorithm : group;
|
import std.algorithm : group;
|
||||||
|
@ -1154,14 +1208,18 @@ import std.range : chain, retro, front, retro;
|
||||||
|
|
||||||
### Scope guards
|
### Scope guards
|
||||||
|
|
||||||
Scope guards allow executing statements at certain conditions if the current block is left:
|
Scope guards allow executing statements at certain conditions if the current
|
||||||
|
block is left:
|
||||||
|
|
||||||
* `scope(exit)` will always call the statements.
|
* `scope(exit)` will always call the statements.
|
||||||
* `scope(success)` statements are called when no exceptions have been thrown.
|
* `scope(success)` statements are called when no exceptions have been thrown.
|
||||||
* `scope(failure)` denotes statements that will be called when an exception
|
* `scope(failure)` denotes statements that will be called when an exception
|
||||||
has been thrown before the block's end.
|
has been thrown before the block's end.
|
||||||
|
|
||||||
Using scope guards makes code much cleaner and allows to place resource allocation and clean up code next to each other. They also improve safety because they make sure certain cleanup code is always called independent of which paths are actually taken at runtime.
|
Using scope guards makes code much cleaner and allows to place resource
|
||||||
|
allocation and clean up code next to each other. They also improve safety
|
||||||
|
because they make sure certain cleanup code is always called independent of
|
||||||
|
which paths are actually taken at runtime.
|
||||||
|
|
||||||
Scope guards are called in the reverse order they are defined.
|
Scope guards are called in the reverse order they are defined.
|
||||||
|
|
||||||
|
@ -1174,3 +1232,130 @@ void foo() {
|
||||||
p is freed anyways when leaving the scope */
|
p is freed anyways when leaving the scope */
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Range algorithms
|
||||||
|
|
||||||
|
The modules `std.range` and `std.algorith` contain many functions which can be
|
||||||
|
composed to express complex operations in a readable way. They are based on
|
||||||
|
ranges and will work on your datatypes, if they implement the range interface.
|
||||||
|
|
||||||
|
```D
|
||||||
|
// Hey come on, just get the whole army!
|
||||||
|
import std.algorithm : canFind, map,
|
||||||
|
filter, sort, uniq, joiner, chunkBy, splitter;
|
||||||
|
import std.array : array, empty;
|
||||||
|
import std.range : zip;
|
||||||
|
import std.stdio : writeln;
|
||||||
|
import std.string : format;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
string text = q{This tour will give you an
|
||||||
|
overview of this powerful and expressive systems
|
||||||
|
programming language which compiles directly
|
||||||
|
to efficient, *native* machine code.};
|
||||||
|
|
||||||
|
// splitting predicate
|
||||||
|
alias pred = c => canFind(" ,.\n", c);
|
||||||
|
// as a good algorithm it just works lazily without allocating memory!
|
||||||
|
auto words = text.splitter!pred
|
||||||
|
.filter!(a => !a.empty);
|
||||||
|
|
||||||
|
auto wordCharCounts = words
|
||||||
|
.map!"a.count";
|
||||||
|
|
||||||
|
// Output the character count per word in a nice way beginning with least chars!
|
||||||
|
zip(wordCharCounts, words)
|
||||||
|
// convert to array for sorting
|
||||||
|
.array()
|
||||||
|
.sort()
|
||||||
|
// we don't need duplication, right?
|
||||||
|
.uniq()
|
||||||
|
// put all in one row that have the same char count.
|
||||||
|
// chunkBy helps us here by generating a range of ranges
|
||||||
|
// that are chunked by the length
|
||||||
|
.chunkBy!(a => a[0])
|
||||||
|
// those elments will be joined
|
||||||
|
// on one line
|
||||||
|
.map!(chunk => format("%d -> %s",
|
||||||
|
chunk[0],
|
||||||
|
// just the words
|
||||||
|
chunk[1]
|
||||||
|
.map!(a => a[1])
|
||||||
|
.joiner(", ")))
|
||||||
|
// joiner joins, but lazily!
|
||||||
|
// and now the lines with the line feed
|
||||||
|
.joiner("\n")
|
||||||
|
// pipe to stdout
|
||||||
|
.writeln();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unittesting
|
||||||
|
|
||||||
|
D has built-in unittests via a very simple syntax, which can appear anywhere in
|
||||||
|
a D module:
|
||||||
|
|
||||||
|
```D
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
assert(myAbs(-1) == 1);
|
||||||
|
assert(myAbs(1) == 1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```D
|
||||||
|
import std.stdio : writeln;
|
||||||
|
|
||||||
|
struct Vector3 {
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
double z;
|
||||||
|
|
||||||
|
double dot(Vector3 rhs) const {
|
||||||
|
return x * rhs.x + y * rhs.y + z * rhs.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// That's okay!
|
||||||
|
unittest {
|
||||||
|
assert(Vector3(1,0,0).dot(Vector3(0,1,0)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
string toString() const {
|
||||||
|
import std.string : format;
|
||||||
|
return format("x:%.1f y:%.1f z:%.1f", x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// … and that too!
|
||||||
|
unittest {
|
||||||
|
assert(Vector3(1,0,0).toString() == "x:1.0 y:0.0 z:0.0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
Vector3 vec = Vector3(0,1,0);
|
||||||
|
writeln(`This vector has been tested: `, vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or just somewhere else
|
||||||
|
unittest {
|
||||||
|
Vector3 vec;
|
||||||
|
// .init a special built-in property that
|
||||||
|
// returns the initial value of type.
|
||||||
|
assert(vec.x == double.init);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Executing unittests
|
||||||
|
|
||||||
|
To execute the unittests, just compile with the `-unittest` flag. When running
|
||||||
|
the program, the unittests get run before the main function is executed. When
|
||||||
|
using DUB, just run `dub test`.
|
||||||
|
|
||||||
|
#### Code coverage
|
||||||
|
|
||||||
|
D also supports generating coverage reports out of the box, just compile with
|
||||||
|
the `-cov` flag. When using DUB, just add the flag `--coverage`.
|
||||||
|
|
Loading…
Reference in a new issue