Some notes.
I’m assuming you want to use D largely, but not entirely, for competitive programming. That’s me right now.
Basics
Syntax is very similar. Function definitions, semicolon-terminated statements, variable declarations, and so on. You can declare int main() {...}
or void main() {...}
or something with arguments.
Basic types like bool
and int
and double
are all there. Wonderfully, long
is 64 bits. Instead of unsigned
whatever, just prefix a u
, e.g. uint
.
Arithmetic operators and bit operators are all there too, including unsigned right shift >>>
. Although ^
is still xor, D has exponentiation as ^^
. Sadly, %
is still same-sign remainder; there’s no true mod.
Casts look like cast(int) x;
Control Flow
if
, while
, for
, do
, and even switch
all work as you’d expect, along with break
and continue
.
foreach
is the nice addition though. Not only can you iterate over arrays and stuff, but range loops go like:
If you want both the index and the element (a la zipWithIndex
):
And to modify the element in place:
This ref
is also the syntax to pass something by reference to a function, by the way. Functions are otherwise pretty much the same.
I/O
You have access to scanf
and printf
, for some weird reason.
But D’s alternatives are readf
and writef
. They also have variants writefln
to add a newline. You often won’t even need writef
anyway, since write
and writeln
take any number of arguments and turn them into strings before printing.
Two gotchas with readf
:
-
readf
specifiers don’t skip white space, so where you’d write:you should write:
-
readf
on a string reads to end of input.
So call readln
instead, to read a line: either with no arguments to return a string or with a buffer to read it into there. This also reads the newline, but after import std.string;
you can call s = chomp(s);
to get rid of control characters.
Call functions like stderr.writef(...)
for debug output.
I just want my int-scanning macro so I don’t forget my ampersands, please
I can do better: readf
infers the type of whatever you give it. This variadic magic fits on an 80-character line. After seeing this, just thinking about cstdio
code makes me angry.
Arrays
int[100008] a;
gives you a static (fixed-length) array.
int[] b;
gives you a dynamic (variable-length) array. It’s sort of like C++’s STL vector
. Actually, it’s properly called a slice, and you can read more about it in the D Slices article, which gets linked quite often in the forum.
Unfortunately after a tragic experience on Codeforces, I’ve discovered its reallocation semantics seems somewhat slow when compared to similar uses of C++ vector
for one big problem: after you pop an element off the back or otherwise decrease the length, the slice must be reallocated to insert more elements. So far, I’ve found std.container.Array
better fits such needs. But if you only push things in, iterate across them, or delete everything at once, I think slices are okay.
Declare Array
s like:
You use a bang for templates in D.
Anyway, in general, binary ~
is the append or concatenate operator; you can append elements by writing b ~= 3;
or b ~= [5, 7];
For slices, experimenting shows that D does roughly double the slice after each reallocation so that this is amortized O(1), as you’d hope, although I can’t find any documentation saying so. You can get the length with b.length
.
Array
s, on the other hand, multiply their capacities by 3/2 on reallocation.
Also, if you import std.array;
, you get to do b.front
and b.back
on slices (you don’t need parentheses!), which are refs through which you can modify the element. You also get b.popFront()
and b.popBack()
(which are void
like their C++ STL counterparts, to my disappointment) and b.empty
. There doesn’t appear to be a .clear()
function but you can just set b.length = 0;
. Also, calling .dup
makes a dynamic copy.
Strings (string
) are just an alias for immutable char[]
s. (So if you want to store a string in a char[]
, call .dup
. But you can still ~=
a string to a variable of type string
; it just replaces the variable with a new copy.)
Array
s also have .front
, .back
, .length
, .empty
, indexing and so on. Their popping methods are called .removeFront()
and removeBack()
, however. Array
s do have a .clear()
method, but unlike STL vector
, it throws away any capacity. It appears setting .length = 0
is slightly faster if you expect to make the Array
large again soon.
Deques, etc.
This is so far the one place D has disappointed me relative to C++, and only mildly. Your best bet is DList
(doubly-linked list) from std.container
. The type looks like DList!int
. As above, you can .front
, .popFront()
, and also .insertFront(x)
, and same for back
and cognates, of course.
You can also .empty
, but sadly, you can’t get the length of a DList
. But now that I think about it, .empty
is often enough anyway if you’re BFSing or whatever, and you can always maintain the length yourself somewhere.
You can declare associative arrays by putting stuff between the same square brackets you used for arrays. These are hash tables, not ordered. The type is just e.g.
Test for key inclusion Pythonically with in
, as:
It also supports .length
; .keys
and .values
(dynamic array copies); .byKey()
and .byValue()
and .byKeyValue()
(iterable-ish things, not copies); .get
(pass a second default value to return if )
There is a RedBlackTree
and a BinaryHeap
in std.container
.
Structs
As you’d expect, but no semicolon.
You get ==
and a constructor Pt(42, 1337)
for free.
Debug
Sweet: just surround a block with debug
.
Compile with -debug
to enable.
You also get assert(...);
, which is normally enabled; you compile with -release
to disable.
Pairs/Tuples
Not as beautiful as I’d hoped, but certainly not worse than C++. The import is
The type looks like Tuple!(int, string)
. Construct as tuple(1337, "hi")
. Access elements with [0]
, [1]
and so on.
The nice thing is that these are real tuples that can hold more than two things, I guess.
Also, interestingly, you can foreach
-iterate across a tuple, which gets expanded at compile-time.
Alias
D’s alternatives to typedef
s and some other things.
alias Weight = double;
Sorting and Other Algorithms
No s.begin(), s.end()
nonsense. Just sort(array)
. Optionally, you can specify a comparator with either a string or a function:
sort!"a > b"(array);
sort!((x, y) => x > y)(array);
bool myComp(int x, int y) @safe pure nothrow { return x > y; }
sort!myComp(array);
The string made me kind of uncomfortable at first, but I guess that’s my reflex to JavaScript string callbacks and the same concerns don’t apply here. There’s no eval
ing, it’s all compile time, and local variables don’t leak; any expression involving a
and b
works.
What else is there? min
and max
, which support more than 2 arguments; swap
; fill(array, val)
; sum
.
Extra Things
Inside indexes, $
is a shortcut for array length, so a[$-1]
is the last element. You can do stuff like a[$/2]
too.
D has const
, which has the same meaning as in C++ (you can’t modify the variable through this pointer) but it also has two more powerful modifiers: immutable
(this variable is guaranteed not-modifiable through any pointer) and enum
(compile-time constants, as if #define
d). If you write enum x = ...
you can force D to calculate the expression at compile-time, even if it’s very complicated involving function calls and whatnot
Other function parameter modifiers than ref
include in
, out
, lazy
.
The case
s of switch
has some extra goodies. You can write
or
Also, remember how you strangely import stuff from std.array
and then you can call things on arrays? This is actually Uniform Function Call Syntax (UFCS) sugar; you can write array.front
to call front(array)
. Of course, you can directly call front(array)
too. This is how the std.array
functions work.
You can even do it yourself, although I probably wouldn’t advise it:
There are functional goodies filter
, map
, reduce
, which are also called like sort
, with the bang !
template-ish syntax. UFCS lets you chain functional goodies like array.map!"a + 1".filter!"a > 5"
if you want to. You can call reduce(seed, array)
(= foldl) or reduce(array)
(= foldl1).
These goodies work on “ranges”, not just arrays, which are things that allow sequential access in general, like how C++ uses pairs of generators. Note that filter
and map
return lazy ranges; you can call array
on the results (after importing std.array
) to convert it to an array.
std.range
has lots of other more basic things you’d expect in a functional language:
take
,drop
,repeat
,cycle
(all with the same meaning as in Haskell)retro
is a reversed version of a range like Python’sreversed
chain(...)
chains two or more ranges together like Python’sitertools.chain
chunks(n)
cuts the source into chunks of length nonly
yields a range of its varargs.
The function to
from std.conv
does conversions between types, and is quite good at it, e.g. to!int
and to!string
.
Operator overloading is a bit weird (but see D’s rationale):
import std.stdio;
import std.algorithm;
import std.array;
struct Pt {
int x, y;
Pt opBinary(string op)(in Pt o)
if (op == "+") // this is checked at compile time
{
return Pt(x + o.x, y + o.x);
}
int opCmp()(const ref Pt o) const {
if (x != o.x) return x < o.x ? -1 : 1;
if (y != o.y) return y < o.y ? -1 : 1;
return 0;
}
}
void main() {
Pt[] ps = [ Pt(1, 2), Pt(3, 4), Pt(1, 5), Pt(3, 2) ];
sort(ps);
writeln(ps);
ps = ps.map!(a => a + Pt(100, 100)).array;
Pt res = ps.reduce!"a + b";
writeln(ps);
writeln(res);
}
opCmp
is three-valued, and the comparison operators <
, <=
, >
, >=
are derived from it. Unfortunately, there doesn’t seem to be a more convenient way to write this that is still as fast; you can do cmp(only(x, y), only(o.x, o.y))
, using cmp
from std.algorithm
for lexicographical comparison and only
from std.range
to create a lazy range, but it still takes 3 to 4 times as much time. (This version is then 3 to 4 times faster than if you cmp([x, y], [o.x, o.y])
.)
Pleasingly, structs have a built-in definition of ==
.
Note, by the way, we can’t use map!"a + Pt(100,100)"
because “Pt” isn’t available in the thing that compiles the string into the function.
Template
Okay, I added a few more imports, but this is still much shorter than my C++ template.
I don’t need any of the range-loop macros or scanning macros or debug macros or (s).begin(), (s).end()
macros or vector-dumping function (vectors have nice to!string
already) or typedefs.
I might need to add a minify
and maxify
, but the definitions of those will be shorter than C++ too. w00t. For now (this’ll get updated later):
import std.stdio, std.array, std.range, std.string, std.typecons;
import std.algorithm, std.container, std.math, std.numeric, std.random;
void scan(T...)(ref T args) { foreach (ref arg; args) readf(" %s", &arg); }
void minify(T)(ref T a, in T b) { if (a > b) a = b; }
void maxify(T)(ref T a, in T b) { if (a < b) a = b; }
void ewriteln(T...)(T args) { stderr.writeln("\033[35m", args, "\033[0m"); }
int ilen(T)(const ref T a) { return cast(int)(T.length); }