add sections about compiling, range algorithms and unittesting

This commit is contained in:
Johannes Loher 2018-05-30 16:12:38 +02:00
parent e1a4ec035e
commit 9b06a68383

View file

@ -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 projects 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 projects 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`.