Cours de programmation langage D


Télécharger Cours de programmation langage D

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


Cours de programmation langage D [ Eng]

"D" iving dans

Vous savez ce qui vient en premier, donc sans plus tarder:

importer std.stdio;

void main () {

writeln ("Bonjour, monde!");

}

Selon ce que vous connaissez d'autres langues, vous pourriez avoir un sentiment de doute, une légère appréciation de la simplicité, ou peut-être une légère déception que D ne soit pas allé dans les langages de script pour permettre des déclarations de haut niveau. (Les instructions de niveau supérieur invitent les variables globales, qui se transforment rapidement en responsabilité à mesure que le programme se développe, D offre des façons d'exécuter le code en dehors de la ligne principale, de manière plus structurée.) Si vous êtes un adepte de la précision, être soulagé d'entendre que void main est équivalent à int main qui renvoie "success" (code zéro) au système d'exploitation s'il termine avec succès l'exécution.

Mais n'allons pas nous devancer. Le but de la traditionnelle "Bonjour, monde!" programme n'est pas pour discuter des capacités d'une langue, mais plutôt pour vous lancer dans l'écriture et l'exécution de programmes utilisant cette langue. Si vous n'avez pas de versions transparentes offrant des versions transparentes, la ligne de commande est un chemin facile. Après avoir tapé le code ci-dessus dans un fichier appelé, dites, bonjour. d, lancez un shell et tapez les commandes suivantes:

$ dmd hello.d

$ ./hello

Bonjour le monde!

$ _

où $ représente votre invite de commande (il peut s'agir de C: \ Path \ To \ Dir> sous Windows ou / path / to / dir% sur les systèmes Unix, tels que OSX, Linux et Cygwin). Vous pouvez même obtenir le programme à compiler et à exécuter automatiquement si vous appliquez un peu de vos compétences système-fu. Sous Windows, vous pouvez associer la commande shell "Run" au programme rdmd. exe, qui fait partie de l'installation. Les systèmes de type Unix supportent la "notation shebang" pour le lancement des scripts, syntaxe que D comprend; ajouter la ligne

#! / usr / bin / rdmd

au tout début de votre salut. d programme le rend directement exécutable. Après avoir apporté cette modification, vous pouvez simplement taper à l'invite de commande:

$ chmod u + x hello.d

$ ./hello.d

Bonjour le monde!

$ _

(Vous devez faire la chose chmod une seule fois.)

Sur tous les systèmes d'exploitation, le programme rdmd est assez intelligent pour mettre en cache l'exécutable généré, de sorte que la compilation est réellement effectuée seulement après que vous avez changé le programme, pas chaque fois que vous l'exécutez. Ceci, combiné au fait que le compilateur proprement dit est très rapide, favorise un cycle d'édition rapide qui aide les scripts courts et les grands programmes.

Le programme commence par la directive

importer std.stdio;

qui demande au compilateur de rechercher un module appelé std. stdio et rendre ses symboles disponibles pour l'utilisation. import est semblable à la directive #include du préprocesseur trouvée en C et C ++ mais est plus proche en sémantique de l'import de Python: il n'y a pas d'inclusion textuelle - juste une acquisition de table de symboles. Les importations répétées du même fichier sont sans importation. Selon la tradition vénérable établie par C, un programme D consiste en une collection de déclarations réparties sur plusieurs fichiers. Les déclarations peuvent introduire, entre autres, des types, des fonctions et des données. Notre premier programme définit la fonction principale de ne prendre aucun argument et de retourner "le néant" - c'est-à-dire vide. Lorsqu'elle est invoquée, main appelle la fonction writeln (qui, bien sûr, a été astucieusement définie par le module std.stdio), en lui passant une chaîne constante. Le suffixe In indique que writeln ajoute une nouvelle ligne au texte imprimé. Les sections suivantes offrent un accès rapide à Deeville. De petits programmes illustratifs introduisent des concepts de base du langage. À ce stade, l'accent est mis sur la transmission d'une sensation pour la langue, plutôt que de donner des définitions pédantes. Les chapitres suivants traiteront chaque partie de la langue plus en détail.

1.1 Nombres et expressions

Êtes-vous curieux de savoir quelle est la taille des étrangers? Écrivons un programme simple qui affiche

une gamme de hauteurs habituelles en pieds + pouces et en centimètres.

/ *

Calculer les hauteurs en centimètres pour une gamme de hauteurs exprimées en pieds et en pouces

* /

importer std.stdio;

void main () {

// Valeurs peu susceptibles de changer bientôt

inchesPerFoot immuable = 12;

cmPerInch immuable = 2,54;

// Loop'n write

foreach (pieds; 5 .. 7) {

foreach (pouces; 0 inchesPerFoot) {

writeln (pieds, "'", pouces, "" \ t ",

(feet * inchesPerFoot + pouces) * cmPerInch);

}

Une fois exécuté, ce programme imprimera une belle liste de deux colonnes:

5'0 "152,4

5'1 "154,94

5'2 "157,48

6'10 "208.28

6'11 "210.82

La construction foreach (f eet; 5 .. 7) {. . . } est une instruction d'itération qui définit une variable entière feet et la lie à tour de rôle à 5 puis à 6, mais pas à 7 (l'intervalle est ouvert vers la droite).

Tout comme Java, C ++ et C #, D prend en charge / * les commentaires muitiline * / et

// commentaires de ligne unique (plus les commentaires de documentation, que nous verrons plus tard).

Un autre détail intéressant est la façon dont notre petit programme introduit ses données. Premièrement, il y a deux constantes:

inchesPerFoot immuable = 12;

cmPerInch immuable = 2,54;

Les constantes qui ne changeront jamais sont introduites avec le mot clé immutable. Les constantes, ainsi que les variables, n'ont pas besoin d'un type de manifeste; le type réel peut être déduit de la valeur avec laquelle le symbole est initialisé. Dans ce cas, le littéral 12 indique au compilateur que dans c hes Pe rFoot est un entier (noté D avec l'int familier); De même, le 2.54 littéral fait que cmPerInch est une constante à virgule flottante (de type double). En allant de l'avant, nous remarquons que les définitions des pieds et des pouces profitent de la même magie, parce qu'elles ressemblent à des variables tout à fait correctes, mais n'ont pas de parures explicites. Cela ne rend pas le programme moins sûr que celui qui stipule:

immutable int inchesPerFoot = 12;

cmPerInch double immuable = 2,54;

foreach (pieds int; 5 .. 7) {

}

et ainsi de suite, seulement moins redondant. Le compilateur permet d'omettre des déclarations de type uniquement lorsque les types peuvent être déduits sans ambiguïté à partir du contexte. Mais maintenant que ces types sont apparus, faisons une pause d'une minute et voyons quels types numériques sont disponibles. Dans l'ordre de la taille croissante, les types intégraux signés comprennent byte, short, int et tong, ayant respectivement des tailles de 8, 16, 32 et 64 bits. Chacun de ces types a une contrepartie non signée de la même taille, nommée suivant une règle simple: ubyte, usho rt, uint et ulong. (Il n'y a pas de modificateur "unsigned" comme en C.)



Les types à virgule flottante sont constitués de float (nombre à simple précision IEEE 754 32 bits), double (IEEE 754 à 64 bits) et real (aussi grand que les registres à virgule flottante de la machine peuvent aller, mais pas moins de 64 bits: par exemple, sur les machines Intel, le format réel est un format 79 bits double extension IEEE 754). Pour en revenir au royaume des nombres entiers, des littéraux tels que 42 peuvent être affectés à n'importe quel type numérique, mais notez que le Le compilateur vérifie si le type de cible est réellement assez grand pour contenir cette valeur.

Donc la déclaration immutable byte inchesPerFoot = 12;

 est aussi bon que celui qui omet byte car 12 s'adapte aussi confortablement en 8 bits qu'en 32. Par défaut, si le type cible doit être déduit du nombre (comme dans l'exemple de programme), les constantes intégrales ont le type int et flo- Les constantes ponctuelles ont un type double. En utilisant ces types, vous pouvez construire beaucoup d'expressions dans D en utilisant des opérateurs et des fonctions arithmétiques. Les opérateurs et leur précédence ressemblent beaucoup à ceux que vous trouverez dans les langages frères de D: +, *, /, et% pour l'arithmétique de base, ==,! =,, <=,> = Pour les comparaisons, f un (un rgument 1, un document 2) pour les appels de fonction, et ainsi de suite. Pour en revenir à notre programme de centimètres à pouces, il y a deux détails importants sur l'appel à l'écriture. L'un est que writeln prend cinq arguments (par opposition à un dans le programme qui a ouvert les fréquences d'appel). Tout comme les fonctions d'E / S trouvées dans Pascal (writeln), C (printf) ou C ++ (tout), la fonction writeln de D accepte un nombre variable d'arguments (c'est une "fonction variadique"). En D, cependant, les utilisateurs peuvent définir leurs propres fonctions variadiques (contrairement à Pascal) qui sont toujours de type sécurisé (contrairement à C) sans avoir besoin de détourner gratuitement les opérateurs (contrairement au C ++). L'autre détail est que notre appel à writeln mélange maladroitement des informations de formatage avec les données en cours de formatage. Il est souvent souhaitable de séparer les données de la présentation. Utilisons plutôt la fonction d'écriture formatée w ri t efln:

writefln ("% s '% s" \ t% s ", pieds, pouces,

(pieds * poucesPerFoot + pouces) * cmPerInch),

L'appel nouvellement arrangé produit exactement la même sortie, à la différence que le premier argument de writefln décrit entièrement le format. % introduit un spécificateur de format similaire à printf de C, par exemple,% i pour les entiers,% f pour les nombres à virgule flottante et% s pour les chaînes. Si vous avez utilisé printf, vous vous sentirez comme à la maison sans un détail étrange: nous imprimons des int et des doubles ici ... comment se fait-il qu'ils soient tous deux décrits avec le spécificateur% s, qui décrit traditionnellement seulement les cordes? La réponse est simple. La fonction d'argument variadique de D donne à writefln un accès aux types d'arguments passés, une configuration qui a deux conséquences: (1) la signification de% s peut être étendue à "quelle que soit la représentation par défaut de l'argument" et (2) ne correspondent pas au spécificateur de format avec les types d'arguments réels, vous obtenez une erreur épurée au lieu du comportement bizarre spécifique aux appels printf mal formatés (sans parler des exploits de sécurité rendus possibles par les appels printf avec des chaînes de format non fiables).

1.2 Déclarations

Dans D, tout comme dans les langues apparentées, toute expression suivie d'un point-virgule est une déclaration (par exemple, l'appel du programme "Hello, world!" À writeln a un: juste après). L'effet de l'instruction est d'évaluer simplement l'expression. D est un membre de la famille des "accolades à blocs bouclés", ce qui signifie que vous pouvez regrouper plusieurs déclarations en une en les entourant de {et} â € "quelque chose qui est nécessaire, par exemple, quand vous voulez faire plusieurs choses à l'intérieur d'afo atteindre la boucle. Dans le cas d'une seule déclaration, vous pouvez omettre entièrement les accolades. En fait, toute notre double boucle de conversion en hauteur pourrait être réécrite comme suit:

foreach (pieds; 5 .. 7) '

foreach (pouces; 0 poucesPerFoot)

writefln ("% s '% s" \ t% s ", pieds, pouces,

(feet * inchesPerFoot + pouces) - * cmPerInch);

Omettre des accolades pour des instructions simples a l'avantage de raccourcir le code et de désavantager les modifications (lors de la maintenance du code, vous devrez ajouter ou supprimer des accolades lorsque vous manipulez des instructions). Les gens ont tendance à être assez divisés quand il s'agit de règles pour l'indentation et pour placer des accolades. En fait, tant que vous êtes cohérent, ces choses ne sont pas aussi importantes qu'elles peuvent sembler, et comme preuve, le style utilisé dans ce livre (contreventement complet même pour les instructions simples, ouverture des accolades sur la ligne d'introduction, et fermeture accolades sur leurs propres lignes) est, pour des raisons typographiques, tout à fait différent du style de l'auteur dans le code quotidien. S'il pouvait le faire sans se transformer en loup-garou, n'importe qui pourrait le faire. Le langage Python a popularisé un style différent d'expression de la structure des blocs au moyen de l'indentation - la forme suit la structure à son meilleur. L'espace qui compte est une proposition étrange pour les programmeurs de certaines autres langues, mais les programmeurs Python ne jurent que par cela. D ignore normalement les espaces mais est spécialement conçu pour être facilement analysé (par exemple, l'analyse n'a pas besoin de comprendre la signification des symboles), ce qui suggère qu'un joli projet pourrait implémenter un simple préprocesseur permettant l'utilisation du style d'indentation Python avec D sans souffrir inconvénient dans le processus de compilation, d'exécution et de débogage des programmes.

Les exemples de code ci-dessus ont également introduit l'instruction if. La forme générale devrait être très familière:

if () else

Un bon résultat théorique connu sous le nom de théorème de la structure [10] prouve que nous pouvons implémenter n'importe quel algorithme en utilisant des instructions composées, si des tests, et des boucles a la for et f o rea ch. Bien sûr, tout langage réaliste offrirait plus que cela, et D ne fait pas exception, mais pour le moment, déclarons-nous content dans la mesure où les déclarations vont et vont de l'avant.

1.3 Les bases de la fonction

Allons au-delà de la définition de la fonction principale et voyons comment définir d'autres fonctions dans D. Les définitions de fonctions suivent le modèle trouvé dans d'autres langages de type Algol: d'abord le type de retour, puis le nom de la fonction et enfin les paramètres formels comme une liste entre parenthèses séparées par des virgules. Par exemple, pour définir une fonction appelée pow qui prend un double et un int et renvoie un double, vous écririez

double pow (double base, exposant int) {

}

Chaque paramètre de fonction (base et exposant dans l'exemple ci-dessus) a, en plus de son type, une classe de stockage optionnelle qui décide de la façon dont les arguments sont passés à la fonction lorsqu'elle est invoquée. Par défaut, les arguments sont passés en pow par valeur. Si la classe de stockage ref est préfixée au type d'un paramètre, le paramètre est lié directement à l'argument entrant de sorte que la modification du paramètre soit directement et immédiatement répercutée dans la valeur reçue de l'extérieur. Par exemple:

importer std.stdio;

void fun (ref uint x, double y) {

x = 42;

y = 3,14;

}

void main () {

uint a = 1;

double b = 2;

amusant (a, b);

writeln (a, "", b);

}

Ce programme imprime 42 2 parce que x est une ref uinte, ce qui signifie qu'assigner x signifie vraiment assigner a. D'un autre côté, assigner à y n'a aucun effet sur b parce que y est une copie privée à disposition. Les derniers ornements dont nous discuterons dans cette brève introduction sont in and out. En termes simples, il s'agit d'une promesse de la part de la fonction qu'elle veut seulement regarder, ne pas toucher, le paramètre. L'utilisation de out avec un paramètre de fonction fonctionne de manière similaire à ref, avec la modification que le paramètre est initialisé de force à sa valeur par défaut lors de l'entrée de la fonction. (Chaque type T définit une valeur initiale, notée T. init. Les types définis par l'utilisateur peuvent définir leur propre init.) Il y a beaucoup plus à dire sur les fonctions. Vous pouvez passer des fonctions à d'autres fonctions, les imbriquer les unes dans les autres, permettre à une fonction de sauvegarder son environnement local (fermetures syntaxiques à part entière), créer et manipuler confortablement des fonctions non nommées (lambdas), et quelques petits morceaux juteux supplémentaires. Nous allons arriver à chacun d'eux en bon ordre.



1.4 Tableaux et tableaux associatifs

Les tableaux et les tableaux associatifs (ces derniers familièrement appelés hashtables ou hashs) sont sans doute les structures de données composées les plus utilisées dans l'histoire de l'informatique, jalousement suivies par les listes de Lisp. Beaucoup de programmes utiles n'ont besoin que d'une sorte de tableau et de tableau associatif, il est donc temps de voir comment D les implémente.

1.4.1 Construire un vocabulaire

Par exemple, écrivons un programme simple en suivant cette spécification:

Lire un texte composé de mots séparés par des espaces et associer un nombre unique à chaque mot distinct. Lignes de sortie du mot d'ID de formulaire.

Ce petit script peut être très utile si vous voulez faire du traitement de texte; Une fois que vous avez construit un vocabulaire, il vous suffit de manipuler des nombres (moins chers), pas des mots pleins. Une approche possible pour construire un tel vocabulaire est d'accumuler des mots déjà vus dans une sorte de dictionnaire qui met en correspondance les mots avec des entiers. Lors de l'ajout d'un nouveau mappage, nous devons seulement nous assurer que l'entier est unique (une option solide consiste simplement à utiliser la longueur actuelle du dictionnaire, ce qui donne les ID 0, 1, 2, ...). Voyons comment nous pouvons le faire en D.

import std.stdio, std.string;

void main () {

uint [string] dictionnaire;

foreach (ligne; stdin.byLine ()) {

// Rompre la phrase en mots

// Ajouter chaque mot de la phrase au vocabulaire

foreach (mot, splitter (strip (ligne))) {

si (mot dans le dictionnaire) continue; // Rien à faire

auto newDD = dictionnaire.longueur;

dictionnaire [mot] = newlD;

writeln (newlD, mot);

}

}

}

Dans D, le type d'un tableau associatif (une table de hachage) qui mappe les valeurs de type K aux valeurs de type V est noté V [K]. Ainsi, la variable dictionnair de type uint [st ring] mappe les chaînes à des entiers non signés - juste ce dont nous avions besoin pour stocker les correspondances mot-à-ID. Le mot d'expression dans le dictionnaire est différent de zéro si le mot clé peut être trouvé dans le dictionnaire de tableau associatif. Enfin, l'insertion dans le dictionnaire est faite avec le dictionnaire [word] = newlD. Bien que cela ne soit pas explicite dans le script ci-dessus, la chaîne de caractères est en réalité un tableau de caractères. Généralement, les tableaux de taille dynamique de T sont notés T [] et sont attribués de plusieurs façons, telles que

int [] a = nouvel int [20]; // 20 entiers initialisés zéro

int [] b = [1, 2, 3]; // Un tableau contenant 1, 2 et 3

Contrairement aux tableaux C, les tableaux D connaissent leur propre longueur, accessible sous la forme arr.length pour tout tableau a rr. Attribuer à arr.lengt h réalloue le tableau. Les accès aux tableaux sont des bornes vérifiées; Le code qui aime prendre le risque de dépassements de tampon peut effrayer le pointeur hors du tableau (en utilisant un rr. pt r), puis utiliser l'arithmétique de pointeur non cochée. En outre, une option de compilateur désactive les limites en vérifiant si vous avez vraiment besoin de tout ce que la plaquette de silicium pourrait donner. Cela place le chemin de la moindre résistance du côté droit de la sécurité: le code est sûr par défaut et peut être rendu un peu plus rapide avec plus de travail.

à ¢ â,¬Â

1.6 Interfaces et classes

Les fonctionnalités orientées objet sont importantes pour les grands projets. par conséquent, les introduire au moyen de petits exemples est à haut risque de regarder maladroite. Ajoutez à cela un désir pressant de rester à l'écart des exemples surutilisés mettant en vedette des formes, des animaux ou des employés, et nous sommes confrontés à un peu de pétrin. Oh, et il y a encore une chose - de petits exemples passent généralement sous silence le problème de la création d'objets polymorphes, ce qui est important. Parlez du bloc de l'écrivain! Heureusement, le monde réel fournit un exemple utile sous la forme d'un problème relativement petit, mais qui n'a pas de solution procédurale satisfaisante. Le code dont nous parlerons ci-dessous est la réécriture d'un petit script awk utile qui a bien dépassé les limites implicites définies par sa conception. Nous travaillerons ensemble vers une solution orientée objet, à la fois petite, complète et élégante. Envisagez d'écrire un petit programme de statistiques appelé stats avec une interface simple: stats prend les fonctions statistiques pour calculer en tant que paramètres de ligne de commande, rassemble les nombres à utiliser via une entrée standard en une liste séparée par des espaces et affiche les résultats statistiques par ligne . Voici un exemple de session:

$ echo 3 5 1.3 4 10 4.5 1 5 1 stats Min Max Moyenne

1

dix

4.225

$ _

Un script rapide et sale peut effectuer de telles tâches sans problème, mais le "sale" a tendance à éclipser le "rapide" à mesure que le nombre de fonctions statistiques augmente. Alors, mettons en place une meilleure solution. Pour l'instant, nous commençons par les fonctions statistiques les plus simples: minimum, maximum et moyenne. Après avoir trouvé un design extensible, la porte est ouverte à l'implémentation de fonctions statistiques plus complexes. Une manière simple d'aborder les choses consiste simplement à parcourir l'entrée et à calculer toutes les statistiques nécessaires. Ce n'est pas une conception évolutive car chaque fois que nous devons ajouter une nouvelle fonction statistique, nous devons opérer du code existant. Les modifications seront non triviales si nous voulons effectuer uniquement les calculs demandés dans la ligne de commande. Idéalement, nous confinerions chaque fonction statistique à un morceau de code contigu. De cette façon, nous pouvons ajouter de nouvelles fonctionnalités au programme en ajoutant simplement le nouveau code - le principe Open-Closed [39] à son meilleur.

Une telle approche implique de comprendre ce que toutes ou au moins la plupart des fonctions statistiques ont en commun, dans le but de les manipuler toutes d'un endroit et d'une manière uniforme. Commençons par remarquer que Min et Max prennent leur entrée un numéro à la fois et ont le résultat à portée de main dès que l'entrée est terminée. Le résultat final est seulement un nombre. En outre, Ave rage doit effectuer une étape de post-traitement (diviser la somme accumulée par le nombre d'entrées). De plus, chaque algorithme maintient son propre état. Lorsque différents calculs obéissent à une interface uniforme et ont besoin de garder l'état, il est logique de faire d'eux des objets et de définir une interface formelle pour manipuler chacun d'entre eux.



50