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)
* [Setup](#setup)
* [Installing DMD and DUB](#installing-dmd-and-dub)
* [OS X](#os-x)
* [Installing with Homebrew (recommended)](#installing-with-homebrew-recommended)
* [Installing locally using the install script](#installing-locally-using-the-install-script)
* [Installing using the installer](#installing-using-the-installer)
* [Windows](#windows)
* [Recommended editor setup](#recommended-editor-setup)
* [Installation of Visual Studio Code](#installation-of-visual-studio-code)
* [Extension setup](#extension-setup)
* [Basics](#basics)
* [Hello World](#hello-world)
* [Imports and modules](#imports-and-modules)G
* [Selective imports](#selective-imports)
* [Scoped imports](#scoped-imports)
* [Imports match files and directories](#imports-match-files-and-directories)
* [Basic Types](#basic-types)
* [Type conversion](#type-conversion)
* [Type properties](#type-properties)
* [Indexing](#indexing)
* [Variable declarations](#variable-declarations)
* [Mutability](#mutability)
* [`immutable`](#immutable)
* [`const`](#const)
* [Functions](#functions)
* [Return type deduction](#return-type-deduction)
* [Default arguments](#default-arguments)
* [Local functions](#local-functions)
* [Memory and pointers](#memory-and-pointers)
* [Memory safety](#memory-safety)
* [Structs](#structs)
* [Member functions](#member-functions)
* [`const` member functions](#const-member-functions)
* [`static` member functions](#static-member-functions)
* [Arrays](#arrays)
* [Static arrays](#static-arrays)
* [Dynamic arrays](#dynamic-arrays)
* [Array operations and properties](#array-operations-and-properties)
* [Slices](#slices)
* [Alias and `string`s](#alias-and-strings)
* [Control flow](#control-flow)
* [if…else](#if%E2%80%A6else)
* [switch…case](#switch%E2%80%A6case)
* [Old fashioned loops](#old-fashioned-loops)
* [Breaking out of outer loops](#breaking-out-of-outer-loops)
* [`foreach` loops](#foreach-loops)
* [Element iteration](#element-iteration)
* [Access by reference](#access-by-reference)
* [Iterate `n` times](#iterate-n-times)
* [Iteration with index counter](#iteration-with-index-counter)
* [Ranges](#ranges)
* [Laziness](#laziness)
* [Copying ranges](#copying-ranges)
* [`RandomAccessRange`s](#randomaccessranges)
* [Lazy range algorithms](#lazy-range-algorithms)
* [Associative arrays](#associative-arrays)
* [Classes](#classes)
* [Inheritance](#inheritance)
* [Final and abstract member functions](#final-and-abstract-member-functions)
* [Checking for identity](#checking-for-identity)
* [Interfaces](#interfaces)
* [Templates](#templates)
* [Template functions](#template-functions)
* [Other templates](#other-templates)
* [Template value parameters](#template-value-parameters)
* [Other template parameters](#other-template-parameters)
* [Delegates](#delegates)
* [Functions as arguments](#functions-as-arguments)
* [Local functions with context](#local-functions-with-context)
* [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
* [Exceptions](#exceptions)
* [`nothrow`](#nothrow)
* [Gems](#gems)
* [Uniform function call syntax (UFCS)](#uniform-function-call-syntax-ufcs)
* [Scope guards](#scope-guards)
- [Hands-On DLang](#hands-on-dlang)
- [Setup](#setup)
- [Installing DMD and DUB](#installing-dmd-and-dub)
- [OS X](#os-x)
- [Installing with Homebrew (recommended)](#installing-with-homebrew-recommended)
- [Installing locally using the install script](#installing-locally-using-the-install-script)
- [Installing using the installer](#installing-using-the-installer)
- [Windows](#windows)
- [Recommended editor setup](#recommended-editor-setup)
- [Installation of Visual Studio Code](#installation-of-visual-studio-code)
- [Extension setup](#extension-setup)
- [Project setup and compiling](#project-setup-and-compiling)
- [Without DUB](#without-dub)
- [With DUB](#with-dub)
- [Basics](#basics)
- [Hello World](#hello-world)
- [Imports and modules](#imports-and-modules)
- [Selective imports](#selective-imports)
- [Scoped imports](#scoped-imports)
- [Imports match files and directories](#imports-match-files-and-directories)
- [Basic Types](#basic-types)
- [Type conversion](#type-conversion)
- [Type properties](#type-properties)
- [Indexing](#indexing)
- [Variable declarations](#variable-declarations)
- [Mutability](#mutability)
- [`immutable`](#immutable)
- [`const`](#const)
- [Functions](#functions)
- [Return type deduction](#return-type-deduction)
- [Default arguments](#default-arguments)
- [Local functions](#local-functions)
- [Memory and pointers](#memory-and-pointers)
- [Memory safety](#memory-safety)
- [Structs](#structs)
- [Member functions](#member-functions)
- [`const` member functions](#const-member-functions)
- [`static` member functions](#static-member-functions)
- [Arrays](#arrays)
- [Static arrays](#static-arrays)
- [Dynamic arrays](#dynamic-arrays)
- [Array operations and properties](#array-operations-and-properties)
- [Slices](#slices)
- [Alias and `string`s](#alias-and-strings)
- [Control flow](#control-flow)
- [if…else](#ifelse)
- [switch…case](#switchcase)
- [Old fashioned loops](#old-fashioned-loops)
- [Breaking out of outer loops](#breaking-out-of-outer-loops)
- [`foreach` loops](#foreach-loops)
- [Element iteration](#element-iteration)
- [Access by reference](#access-by-reference)
- [Iterate `n` times](#iterate-n-times)
- [Iteration with index counter](#iteration-with-index-counter)
- [Ranges](#ranges)
- [Laziness](#laziness)
- [Copying ranges](#copying-ranges)
- [`RandomAccessRange`s](#randomaccessranges)
- [Lazy range algorithms](#lazy-range-algorithms)
- [Associative arrays](#associative-arrays)
- [Classes](#classes)
- [Inheritance](#inheritance)
- [Final and abstract member functions](#final-and-abstract-member-functions)
- [Checking for identity](#checking-for-identity)
- [Interfaces](#interfaces)
- [Templates](#templates)
- [Template functions](#template-functions)
- [Other templates](#other-templates)
- [Template value parameters](#template-value-parameters)
- [Other template parameters](#other-template-parameters)
- [Delegates](#delegates)
- [Functions as arguments](#functions-as-arguments)
- [Local functions with context](#local-functions-with-context)
- [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
- [Exceptions](#exceptions)
- [`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
@ -94,21 +102,21 @@ brew install dub
```bash
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
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd.2.079.0.dmg.
* Open `dmd.2.079.0.dmg`
* Download http://downloads.dlang.org/releases/2.x/2.080.0/dmd.2.080.0.dmg.
* Open `dmd.2.080.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.
#### Windows
* 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
* Download http://downloads.dlang.org/releases/2.x/2.080.0/dmd-2.080.0.exe.
* 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).
### Recommended editor setup
@ -139,6 +147,50 @@ brew cask install visual-studio-code
installed).
* 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
### Hello World
@ -1034,8 +1086,8 @@ doSomething(&add);
#### Local functions with context
To reference member functions or local functions, `delegate`s have to be used. You
can create _closures_ with this:
To reference member functions or local functions, `delegate`s have to be used.
You can create _closures_ with this:
```D
auto foo() {
@ -1135,7 +1187,8 @@ one can write
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
import std.uni: toLower;
@ -1143,7 +1196,8 @@ auto a = "Cool Stuff".toLower;
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
import std.algorithm : group;
@ -1154,14 +1208,18 @@ import std.range : chain, retro, front, retro;
### 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(success)` statements are called when no exceptions have been thrown.
* `scope(failure)` denotes statements that will be called when an exception
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.
@ -1174,3 +1232,130 @@ void foo() {
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`.