--- language: D filename: learnd-fr.d contributors: - ["Nick Papanastasiou", "www.nickpapanastasiou.github.io"] translators: - ["Quentin Ladeveze", "aceawan.eu"] lang: fr-fr --- ```d // Commençons par un classique module hello; import std.stdio; // args n'est pas obligatoire void main(string[] args) { writeln("Bonjour le monde !"); } ``` Si vous êtes comme moi et que vous passez beaucoup trop de temps sur internet, il y a de grandes chances pour que vous ayez déjà entendu parler du [D](http://dlang.org/). D est un langage de programmation moderne, généraliste, multi-paradigmes qui contient des fonctionnalités aussi bien de bas niveau que de haut niveau. D est activement développé par de nombreuses personnes très intelligents, guidées par [Walter Bright](https://fr.wikipedia.org/wiki/Walter_Bright))) et [Andrei Alexandrescu](https://fr.wikipedia.org/wiki/Andrei_Alexandrescu). Après cette petite introduction, jetons un coup d'oeil à quelques exemples. ```d import std.stdio; void main() { //Les conditions et les boucles sont classiques. for(int i = 0; i < 10000; i++) { writeln(i); } // On peut utiliser auto pour inférer automatiquement le // type d'une variable. auto n = 1; // On peut faciliter la lecture des valeurs numériques // en y insérant des `_`. while(n < 10_000) { n += n; } do { n -= (n / 2); } while(n > 0); // For et while sont très utiles, mais en D, on préfère foreach. // Les deux points : '..', créent un intervalle continu de valeurs // incluant la première mais excluant la dernière. foreach(i; 1..1_000_000) { if(n % 2 == 0) writeln(i); } // On peut également utiliser foreach_reverse pour itérer à l'envers. foreach_reverse(i; 1..int.max) { if(n % 2 == 1) { writeln(i); } else { writeln("Non !"); } } } ``` On peut définir de nouveaux types avec les mots-clés `struct`, `class`, `union` et `enum`. Ces types sont passés à la fonction par valeur (ils sont copiés) De plus, on peut utiliser les templates pour rendre toutes ces abstractions génériques. ```d // Ici, 'T' est un paramètre de type. Il est similaire au de C++/C#/Java. struct LinkedList(T) { T data = null; // Utilisez '!' pour instancier un type paramétré. // Encore une fois semblable à '' LinkedList!(T)* next; } class BinTree(T) { T data = null; // S'il n'y a qu'un seul paramètre de template, // on peut s'abstenir de mettre des parenthèses. BinTree!T left; BinTree!T right; } enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, } // Utilisez alias pour créer des abreviations pour les types. alias IntList = LinkedList!int; alias NumTree = BinTree!double; // On peut tout aussi bien créer des templates de function ! T max(T)(T a, T b) { if(a < b) return b; return a; } // On peut utiliser le mot-clé ref pour s'assurer que quelque chose est passé // par référence, et ceci, même si a et b sont d'ordinaire passés par valeur. // Ici ils seront toujours passés par référence à 'swap()'. void swap(T)(ref T a, ref T b) { auto temp = a; a = b; b = temp; } // Avec les templates, on peut également passer des valeurs en paramètres. class Matrix(uint m, uint n, T = int) { T[m] rows; T[n] columns; } auto mat = new Matrix!(3, 3); // T est 'int' par défaut ``` À propos de classes, parlons des propriétés. Une propriété est, en gros, une méthode qui peut se comporter comme une lvalue. On peut donc utiliser la syntaxe des structures classiques (`struct.x = 7`) comme si il s'agissait de méthodes getter ou setter. ```d // Considérons une classe paramétrée avec les types 'T' et 'U' class MyClass(T, U) { T _data; U _other; } // Et des méthodes "getter" et "setter" comme suit: class MyClass(T, U) { T _data; U _other; // Les constructeurs s'appellent toujours 'this'. this(T t, U u) { // Ceci va appeller les setters ci-dessous. data = t; other = u; } // getters @property T data() { return _data; } @property U other() { return _other; } // setters @property void data(T t) { _data = t; } @property void other(U u) { _other = u; } } // Et on l'utilise de cette façon: void main() { auto mc = new MyClass!(int, string)(7, "seven"); // Importer le module 'stdio' de la bibliothèque standard permet // d'écrire dans la console (les imports peuvent être locaux à une portée) import std.stdio; // On appelle les getters pour obtenir les valeurs. writefln("Earlier: data = %d, str = %s", mc.data, mc.other); // On appelle les setter pour assigner de nouvelles valeurs. mc.data = 8; mc.other = "eight"; // On appelle les setter pour obtenir les nouvelles valeurs. writefln("Later: data = %d, str = %s", mc.data, mc.other); } ``` Avec les propriétés, on peut construire nos setters et nos getters comme on le souhaite, tout en gardant une syntaxe très propre, comme si on accédait directement à des membres de la classe. Les autres fonctionnalités orientées objets à notre disposition incluent les interfaces, les classes abstraites, et la surcharge de méthodes. D gère l'héritage comme Java: On ne peut hériter que d'une seule classe et implémenter autant d'interface que voulu. Nous venons d'explorer les fonctionnalités objet du D, mais changeons un peu de domaine. D permet la programmation fonctionelle, avec les fonctions de premier ordre, les fonctions `pures` et les données immuables. De plus, tout vos algorithmes fonctionels favoris (map, reduce, filter) sont disponibles dans le module `std.algorithm`. ```d import std.algorithm : map, filter, reduce; import std.range : iota; // construit un intervalle excluant la dernière valeur. void main() { // On veut un algorithme qui affiche la somme de la liste des carrés // des entiers paires de 1 à 100. Un jeu d'enfant ! // On se contente de passer des expressions lambda en paramètre à des templates. // On peut fournir au template n'importe quelle fonction, mais dans notre // cas, les lambdas sont pratiques. auto num = iota(1, 101).filter!(x => x % 2 == 0) .map!(y => y ^^ 2) .reduce!((a, b) => a + b); writeln(num); } ``` Vous voyez qu'on a calculé `num` comme on le ferait en haskell par exemple ? C'est grâce à une innovation de D qu'on appelle "Uniform Function Call Syntax". Avec l'UFCS, on peut choisir d'écrire un appel à une fonction de manière classique, ou comme un appel à une méthode. Walter Brighter a écrit un article en anglais sur l'UFCS [ici.](http://www.drdobbs.com/cpp/uniform-function-call-syntax/232700394) Pour faire court, on peut appeller une fonction dont le premier paramètre est de type A, comme si c'était une méthode de A. J'aime le parallélisme. Vous aimez le parallélisme ? Bien sûr que vous aimez ça. Voyons comment on le fait en D ! ```d import std.stdio; import std.parallelism : parallel; import std.math : sqrt; void main() { // On veut calculer la racine carrée de tous les nombres // dans notre tableau, et profiter de tous les coeurs // à notre disposition. auto arr = new double[1_000_000]; // On utilise un index et une référence à chaque élément du tableau. // On appelle juste la fonction parallel sur notre tableau ! foreach(i, ref elem; parallel(arr)) { ref = sqrt(i + 1.0); } } ```