Cher lecteur,

Me voici de retour sur le langage D dans sa version 2. Je vais tenter ici de vous apprendre à programmer progressivement. En effet je considère que le langage D est un très bon langage, même pour un débutant. Il existe deux écoles sur ce sujet, ceux préconisant l'apprentissage par un langage de programmation:

  1. haut niveau tel que python
  2. bas niveau tel que le C

La première école a le mérite d'offrir au futur programmeur des progrès rapide au détriment de notion importante sur le fonctionnement de l'ordinateur, les mauvaises habitudes de programmations à éviter, où encore l'enfermement dans un langage ( triste ). La seconde école nécessite une plus grande volonté pour le débutant afin d'arriver à ces fins, mais ceci lui donne l'aptitude de mieux comprendre comment est traduit son programme par l'ordinateur, une plus grande capacité pour apprendre de nouveaux langages.

Le langage java pourrait concilier les 2 écoles mais la compréhension de la programmation orientée objet est un obstacle trop important. Mais voilà le langage D qui vient chambouler cet état. En effet le langage D offre le compromis idéale en simplicité pour l’apprentissage de la programmation. Sur cette courte introduction nous allons commencer à apprendre le langage D. Pour cela vous devez installer les outils nécessaires:

  • ldc, compilateur D
  • pobos, la bibliothèque standard
  • druntime, le ramasse miette
$ su -c 'yum install ldc ldc-druntime ldc-druntime-devel ldc-phobos ldc-phobos-devel'

Je vous conseil en plus (optionnel):

  • ldc-phobos-geany-tags, pour l'auto-complétion dans geany
  • ldc-phobos-devhelp, pour avoir une petite doc de l'api de phobos
$ su -c 'yum install ldc-phobos-geany-tags ldc-phobos-devhelp'

Pour fedora 16 seulement

Pour les personnes souhaitant utilisé le langage D sous Fedora 16 je leur conseil d'installer le dépôt D: . Celui ci corrige de nombreux soucis, en effet il m'est dans l'impossibilité de fournir une mise a jour pour fedora 16 due a des soucis avec llvm-2.9 ceci est corrigé dans fedora-17 ou via le dépôt pour fedora 16

Premier pas

Quel outil utiliser ?

Pour écrire du code en langage D je préconise dans l'ordre:

  1. geany
  2. scite
  3. gedit

Geany

Changer le compilateur D a utiliser: construire->definir les commandes de construction Remplacer seulement dmd par ldc2 et c'est tout :-) .

D'autres outil existe mais ils ne feront pas l'objet de ce chapitre.

Écrire un message dans la console (sortie standard )

A l'aide de votre éditeur préféré ( geany pour moi :-D )


  1. import std.stdio;
  2. import std.string;
  3.  
  4. void main( string[] args ){
  5. write( "Bonjour :-)" );
  6. }

Compilation

Geany

Il suffit d'appuyer sur F8 ;-)

Ligne de commande
$ ldc2 test.d

Exécution

Geany

Il suffit d'appuyer sur F5 ;-)

Ligne de commande
$ ./test
Bonjour :-)

Explication du programme

Afin d'écrire sur la console on doit utiliser la fonction write, cette fonction est définit dans le module std.stdio.

Deuxièmement pour utiliser les chaines de caractères on doit utiliser le type string définit dans le module std.string.

la troisième ligne définit la fonction principale du programme, la fonction main. C'est le point de départ du programme. La fonction main peut recevoir les paramètres de la lignes de commandes. Ces paramètres sont stocké dans le tableau qui se nomme args. Nous verront tout ça plus loin juste retenez que la fonction principale s'écrit de cette manière. Le contenu de la fonction principale est définit par { }. C'est à dire que toutes lignes entre { } de la fonction main seront exécuté ( évalué pour faire du vocabulaire :-) ). Notre fonction principale ne contient qu'une seule ligne write( "Bonjour :-)" ); qui permet d'écrire bonjour dans la console. Note: write écrit sur la même ligne si vous souhaiter un retour à la ligne utiliser writeln.

Faire une addition

On va faire une simple addition, pour cela comme précédemment on va importer de quoi écrire sur la console et écrire la fonction de départ la fonction main.


  1. import std.stdio;
  2. import std.string;
  3.  
  4. void main( string[] args ){
  5. write( "2 + 3 = " );
  6. write( 2 + 3 );
  7. }

Compilation et Exécution

Exactement comme précédemment, je vous renvoie donc au point d'avant. Le résultat:

$ ./test
2 + 3 = 5

Explication du programme

Le programme principale commence par écrire dans la console la chaine de caractère 2 + 3 = la seconde ligne écrit à la suite le résultat de l'addition 2 + 3 soit 5 :-) .

Les types késako ???

On a tous appris tout petit ce qu'est un type mais on a vite oublié tellement que c'est évident :-) . Oui oui rappelez vous les chiffres 1, 2, 3 sont des entiers et 2.5 est un chiffre décimal ( flottant ). L'ordinateur fait comme nous, la distinction entre les différents types voici de suite un exemple


  1. import std.stdio;
  2. import std.string;
  3.  
  4. void main( string[] args ){
  5. int chiffre1 = 2;
  6. int chiffre2 = 3;
  7. write( "2 + 3 = " );
  8. write( chiffre1 + chiffre2 );
  9. }

Alors ce programme fait exactement la même chose que le précédent, c'est à dire qu'il fait l'addition de 2 + 3 :-) .

Ici on a stocké la valeur de 2 dans la variable chiffre1 de type entier, la valeur de 3 stocké dans la variable chiffre2 de type entier. Un entier c'est un chiffre positif ou négatif, si vous n'utilisez que des entiers positif je vous conseil d'utiliser le type size_t.

Information complémentaire: pour les connaisseurs size_t agit comme le type uint sauf qu'il s'adapte aux architectures selon que l'on compile sur un ordinateur 32 bits (uint) ou 64 bits (ulong) de façon automatique.

Faire une fonction

Il est important de découper notre programme en différente fonction. En effet celà permet de ne pa réécrire, par exemple le code effectuant une addition à chaque fois. Le code pour ce travail est écrit une fois et ensuite on l'apelle au besoin. Concrètement cela se traduit par :


  1. import std.stdio;
  2. import std.string;
  3.  
  4. size_t addition( size_t a, size_t b){
  5. return a + b ;
  6. }
  7.  
  8. void main( string[] args ){
  9. size_t chiffre1 = 2;
  10. size_t chiffre2 = 3;
  11. size_t resultat = addition( chiffre1, chiffre2);
  12. write( "2 + 3 = " );
  13. write( resultat );
  14. }

Ici on a définit une fonction qui s’appelle addition. Elle prends deux paramètres qui seront stocké respectivement dans a et b. Ces paramètres doivent êtres des entiers positifs car ils sont typé size_t. Enfin la fontion retourne une valeur de type size_t car avant le nom de la fonction il est écrit size_t.

Surcharger une fonctions

C'est bien jolie la fonction addition! Mais moi, je voudrais additionner des types différents! C'est simple il suffit d'écrire les fonctions avec les types désirés.


  1. import std.stdio;
  2. import std.string;
  3.  
  4. size_t addition( size_t a, size_t b){
  5. return a + b ;
  6. }
  7.  
  8. float addition( size_t a, float b){
  9. return a + b ;
  10. }
  11.  
  12. double addition( double a, size_t b){
  13. return a + b ;
  14. }
  15.  
  16. double addition( double a, double b){
  17. return a + b ;
  18. }
  19.  
  20. void main( string[] args ){
  21. float chiffre1 = 2.6;
  22. size_t chiffre2 = 3;
  23. double chiffre3 = 5;
  24. float resultat1 = addition( chiffre2, chiffre1);
  25. size_t resultat2 = addition( chiffre2, chiffre2);
  26. double resultat3 = addition( chiffre3, chiffre2);
  27. double resultat4 = addition( chiffre3, chiffre3);
  28. witefln( "resultat1: %f | resultat2: %d | resultat3: %f | resultat4: %f ", resultat1, resultat2, resultat3, resultat4 );
  29. }

Le compilateur choisira la fonction addition correspondant aux types soumis. Le nom de la fonction suivie de ces paramètres forme la signature de la fonction. Le type de retour n'est pas compris dans la signature. Noter le f dans writefln f = format %d place un chiffre entier %f place un flottant. Les specifieurs: %d, %f sont les mêmes quen c++:

Les templates

Les fonctions ne permettent pas d'écrire en une fois "une addition c'est comme ça quelque soit le type"! est là qu'intervient les templates pour écrire des fonctions génériques.


  1. import std.stdio;
  2. import std.string;
  3.  
  4. T addition(T,U)( T a, U b){
  5. return a + b ;
  6. }
  7.  
  8. void main( string[] args ){
  9. float chiffre1 = 2.6;
  10. size_t chiffre2 = 3;
  11. float resultat = addition( chiffre1, chiffre2);
  12. write( "2.6 + 3 = " );
  13. write( resultat );
  14. }

Dans notre Exemple le template retournera toujours le même type que le type du prmier paramètre. Ainsi si on inverse l'ordre des paramètres chiffre1 et chiffre2 , le résultat sera de type size_t et donc tronqué (pas de chiffre après la virgule)

Un moyen simple de résoudre ce soucis et de simplement laisser le compilateur de déterminer automatiquement le type de retour. soit simplement:


  1. import std.stdio;
  2. import std.string;
  3.  
  4. auto addition(T,U)( T a, U b){
  5. return a + b ;
  6. }
  7.  
  8. void main( string[] args ){
  9. float chiffre1 = 2.6;
  10. size_t chiffre2 = 3;
  11. float resultat = addition( chiffre1, chiffre2);
  12. write( "2.6 + 3 = " );
  13. write( resultat );
  14. }

La seule modification est l'utilisation du mot clé auto.

Les commentaires

Afin de documenter ton code est pouvoir le comprendre facilement on peut ajouter des commentaires dans le code qui ne seront pas évalué par le compilateur. Un exemple vaut mieux qu'un long discours.


  1. // je suis un commentaire sur une ligne
  2. /*
  3.  je suis un commentaire
  4.  sur plusieurs ligne
  5.  */

Les tableaux

Lorsque l'on développe des applications on utilisent souvent les tableaux. Un tableau va permettre de stocker une suite d'information.


  1. int[] tableau = [1, 2, 3];

On a définit une variable tableau de type int, les signifie que c'est un tableau d'int. Pour accéder au premier élément on commence à compter à partir de 0.


  1. int[] tableau = [1, 2, 3];
  2. writeln( tableau[0] );

Slicing

On peut également récupérer une portion du tableau, par exemple on souhaite récupérer les élément à partir de l'indice 2 jusqu’à la fin:


  1. int[] tableau1 = [1, 2, 3];
  2. int[] tableau2 = tableau1[ 2 .. $];
  3. writeln( tableau2 );

Le symbole $ indique la fin du tableau, si l'on veut l'avant dernier élément on peut faire $ - 1 et ainsi des suite. La longueur d'un tableau peut être connu de cette façon.

Longueur de tableau


  1. int[] tableau1 = [1, 2, 3];
  2. writeln( tableau1.length );

On peut changer la taille d'un tableau:


  1. int[] tableau1 = [1, 2, 3];
  2. tableau1.length = tableau1.length + 5;
  3. tableau1[2 .. $] = [ 4, 5, 6, 7, 8 ];

Concatenation

Pour joindre deux tableaux on utilise le caractère ~


  1. int[] tableau1 = [1, 2, 3];
  2. tableau1 ~= [ 4, 5, 6, 7, 8 ];

Les boucles

Tant que (while)


  1. import std.stdio;
  2.  
  3. void main(){
  4. uint i = 0;
  5. while (i < 10){
  6. writeln(i);
  7. i++;
  8. }
  9. }

Tant que i est plus petit que 10, on boucle et on imprime sur la sortie standard la valeur courante de i.

Faire tant que (do … while)

Cette boucle est semblable à la précédente, seulement ici on garantit au moins une fois le passage dans la boucle.


  1. import std.stdio;
  2.  
  3. void main(){
  4. uint i = 10;
  5. do{
  6. writeln(i);
  7. i++;
  8. }
  9. while(i < 10);
  10. }

Pour (for)

On peut effectuer la déclaration d'une variabl (uint i = 0;)e, définir les conditions de la boucle ( i < 10) et définir une action exécutée à chaque début de boucle (généralement l'incrémentation de cette variable, comme ici: ++i) ,dans la déclaration de la boucle for :


  1. for (uint i = 0; i < 10; ++i){
  2. writeln(i);
  3. }

Pour chaque (foreach)


  1. uint[5] a = [1,5,4,6,8];
  2. foreach(element;a){
  3. writeln(element);
  4. }

Pour chaque élément de a, on imprime sa valeur sur la sortie standard.

On peut également connaître le nombre d'itérations dans la boucle foreach. Par exemple, connaître le numéro de ligne ou l'indice du tableau en cours de traitement.


  1. uint[5] a = [1,5,4,6,8];
  2. foreach(compteur,element;a){ // Notons que element est de même type que a, ici de type uint[]
  3. writefln("numéro %d valeur %d",compteur, element);
  4. }

Aller à (goto)

Le goto n'est à utiliser que dans des cas précis. Il ne faut surtout pas en abuser et la plupart du temps les autres types de boucles suffisent. Depuis le temps que je programme je n'ai eu à l'utiliser qu'une seule fois. Le goto permet d'aller directement à un endroit du code défini par une étiquette :


  1. import std.stdio;
  2.  
  3. void main(){
  4. uint i = 0;
  5. writeln("Bonjour");
  6. monEtiquette:
  7. i++;
  8. writefln("Valeur de i %d", i);
  9. if ( i < 2 ){
  10. goto monEtiquette;
  11. }
  12. writeln("Fin");
  13. }

Les chaines de caractères

Afin d'utiliser les chaines de( caractères, vous devez utiliser le module std.string. Une chaine de caractère commence et se termine par le symbole ", alors qu'un caractère commence et se termine par le symbole '. Soit:


  1. import std.string;
  2.  
  3. string maChaine = "je suis une chaine de caractère supportant l'unicode sans rien faire.";
  4. char caractere = 'c';

Les variables de type string sont immutables cela signigifie que le prochain chapitre, le lire, tu devras (yoda).

Les constantes

Le langage D différentie deux types de constante, const et immutable. const est utilisé pour dire que l'adresse de la variable x ne changera pas. immutable est utilisé pour dire que la valeur de la variable ne changera pas.

le mot clé const renvoie à la notion de pointeur et de référence. l'utilisation des pointeurs en D et pour moi est utile uniquement dans un but de faire une interface d'une application C.

Un peu de code:


  1. immutable int pi = 3.14; // la variable pi ne peut changer de valeur
  2. immutable int[] tableaux = [ 1, 2, 3 ]; // on ne peut pas modifier le contenu du tableau
  3. immutable int[1] tableaux = 4; // erreur on ne peut pas changer la valeur du tableau
  4. immutable int[] tableaux = [ 1, 4, 3]; // mais on peut donner une nouvelle référence

Le type string est immutable par conséquent on ne peut pas faire ça:


  1. string chaine = "Le langage C c'est cool";
  2. chaine[11] = 'D'; // -> Error: chaine[11] isn't mutable

pour cela on fait comme ça:


  1. string chaine = "Le langage C c'est cool";
  2. chaine = chaine[0 .. 11] ~ 'C' ~ chaine[12 .. $];

La différence ici est que l'on change la référence assigner à la variable chaine. Dans un premier temps on construit le tableau de caractère "Le langage D c'est cool" et la référence de ce tableau est assigné à la variable chaine.