Voir les cours et résoudre les problèmes en :
Le C est un langage de programmation impératif conçu pour la programmation système. Inventé au début des années 1970 avec UNIX, C est devenu un des langages les plus utilisés. De nombreux langages plus modernes se sont inspirés de sa syntaxe. Il privilégie la performance sur la simplicité de la syntaxe. [En savoir plus]
Le C++ est un langage de programmation impératif. Inventé au début des années 1980, il apporte de nouveaux concepts au langage C (les objets, la généricité), le modernise et lui ajoute de nombreuses bibliothèques. C++ est devenu l'un des langages les plus utilisés. Sa performance et sa richesse en font le langage de prédilection pour les concours. [En savoir plus]
Pascal est un langage de programmation impératif inventé dans les années 1970 dans un but d'enseignement. Quoiqu'encore utilisé à cette fin, l'absence de bibliothèque standard en limite son utilisation malgré une grande efficacité. Sa syntaxe a été reprise par d'autres langages plus modernes avec plus ou moins de succès. [En savoir plus]


Remarque : Les cours pour ce langage ne sont disponibles que jusqu'au chapitre 4, « Lecture de l'entrée ». Les corrections sont toutefois toujours fournies.
OCaml est un langage de programmation fonctionnel inventé au milieu des années 1990. Il permet aussi une programmation impérative ou objet. Il permet d'écrire des programmes courts et faciles à vérifier et est ainsi utilisé pour certains systèmes embarqués très sensibles comme ceux des avions. Il est utilisé dans l'enseignement en classes préparatoires aux grandes écoles. [En savoir plus]


Remarque : Les cours pour ce langage ne sont disponibles que jusqu'au chapitre 4, « Lecture de l'entrée ». Les corrections sont toutefois toujours fournies.
Java est un langage de programmation impératif et orienté objet. Inventé au début des années 1990, il reprend en grande partie la syntaxe du langage C++ tout en la simplifiant, au prix d'une performance un peu moins bonne. S'exécutant dans une machine virtuelle, il assure une grande portabilité et ses très nombreuses bibliothèques en font un langage très utilisé. On lui reproche toutefois la « verbosité » de son code. [En savoir plus]


Remarque : Pour un débutant souhaitant apprendre Java, nous conseillons fortement de commencer par JavaScool, plus facile à apprendre, bien que fortement similaire.
Java's Cool (alias JavaScool) est conçu spécifiquement pour l'apprentissage des bases de la programmation. Il reprend en grande partie la syntaxe de Java sur laquelle il s'appuie, mais la simplifie pour un apprentissage plus aisé. La plateforme JavaScool est accompagnée d'un ensemble d'activités diverses de découverte de la programmation. [En savoir plus]
Python est un langage de programmation impératif inventé à la fin des années 1980. Il permet une programmation orientée objet et admet une syntaxe concise et claire qui en font un langage très bien adapté aux débutants. Étant un langage interprété, il n'est cependant pas aussi performant que d'autres langages. [En savoir plus]
Juxtaposition

La règle que l'on va annoncer peut sembler déroutante au début, mais on se rendra vite compte qu'en fait on l'utilise déjà sans vraiment le savoir.

Une expression "expr1; expr2", où exrp1 est une expression de type unit, est de la valeur et du type de l'expression expr2.

Tout de suite un exemple. On a vu juste avant que print_string "test" est une expression de type unit, et que 5 était une expression de type int. Donc d'après la règle, la juxtaposition des deux :

print_string "test"; 5

forme une expression de type int, qui vaut 5. Puisque cette expression représente l'entier 5, on va l'afficher avec un print_int, en prenant soin de l'entourer de parenthèses :

print_int (print_string "test"; 5)

Cette ligne forme donc une expression de type unit : c'est notre programme dans son ensemble.

Regardons maintenant ce qu'il se passe lorsqu'on exécute ce programme.

Pour évaluer l'expression expr1; expr2exrp1 est une expression de type unit, on commence par faire l'action de expr1, puis on calcul la valeur de expr2.

Donc pour évaluer print_string "test"; 5, on commence par afficher "test", et ensuite on donne la valeur 5. Ainsi le code :

print_int (print_string "test"; 5)
affiche test5.

Remarque : on peut aussi utiliser une déclaration pour nommer l'expression print_string "test"; 5. Ce code est équivalent au précédent :

let x = print_string "test"; 5 in
print_int x

On précisera le fonctionnement du let dans la partie suivante.

Revenons à la règle. Dans expr1; expr2, lorsque expr1 et expr2 sont tous les deux de type unit, puisque l'ensemble forme une expression du même type que expr2, il est donc de type unit.

Le code suivant est la juxtaposition de deux expressions de type unit, il fait quelque chose :

print_string "test"; print_newline()
Juxtaposition itérée

Voyons maintenant ce qu'il se passe-t-il si l'on juxtapose 3 expressions de type unit.

Par exemple :
print_string "test"; print_int 5; print_string "cinq"

C'est très simple, le compilateur regroupe les deux premières expressions :

( print_string "test"; print_int 5 ) ; print_string "cinq"

Il peut maintenant décortiquer correctement le code à l'aide de la règle énoncé en début de partie. D'abord :

print_string "test"; print_int 5

est la juxtaposition de deux expressions de type unit, donc une expression de type unit.

Ensuite, on va juxtaposer cette expression (qui est déjà elle-même une juxtaposition) avec l'expression print_string "cinq" qui est aussi de type unit. Au final,

print_string "test"; print_int 5 ; print_string "cinq"

est une expression de type unit. C'est un code valide, qui décrit un programme

qui affiche test5cinq.

Dernière chose à voir : une suite de plusieurs expressions de type unit, terminé par une expression d'un autre type que unit. Exemple :

print_string "test"; print_newline(); 5

est une expression de type int, qui vaut 5.

Pour aboutir à ce résultat, le compilateur à simplement lu :

( print_string "test"; print_newline() ) ; 5
D'abord
print_string "test"; print_newline()

est la juxtaposition de deux expressions de type unit, donc est de type unit.

Ensuite

( print_string "test"; print_newline() ) ; 5

est la juxtaposition d'une expression de type unit et d'une autre de type int, et est donc au final une expression de type int.

On peut affiche cette expression avec un print_int :

print_int ( print_string "test"; print_newline(); 5 )

Ce code affiche "test", puis un retour à la ligne, puis 5 :

test
5

On peut généraliser ces deux constructions en disant que :

expr1; expr2; ... exprN-1; exprN

où tous les termes de expr1 à exprN-1 sont forcément de type unit constitue une expression dont la valeur et le type est celui de exprN.

Si une des expressions entre expr1 et exprN-1 n'est pas de type unit, on obtient une erreur. Un exemple :

print_string "test"; 5 ; print_string "cinq"
File "test.ml", line 1, characters 21-22:
Warning: this expression should have type unit.

Le problème est localisé sur le 5 qui devrait être de type unit, mais qui ne l'est pas, puisque 5 est un entier.

Il ne s'agit pas d'une erreur, mais seulement d'un avertissement (warning). Le code fonctionne donc, la valeur 5 est simplement ignorée, et le programme affiche : testcinq.

Bloc d'expression

On peut forcer un certain groupement d'expressions avec des parenthèses ouvrante puis fermante, ou bien avec les bornes begin et end.

Ainsi begin expr1 end comme (expr1) sont simplement équivalents à expr1.

On a déjà vu un exemple :

print_int (print_string "test"; 5)

Voici le même avec begin et end :

print_int begin print_string "test"; 5 end

On reviendra sur les blocs au moment des problèmes de surdéfinition, et aussi pour les structures if.

Déclaration
Énonçons une seconde règle fondamentale, qui pourra elle-aussi sembler un petit peu

déroutante au début.

L'expression let name = expr1 in expr2 vaut expr2 dans laquelle name est un nom qui peut être utilisé dans expr2, et qui représente la valeur de expr1 qu'on calcul une fois pour toutes.

Remarque : le nom name est associée à une valeur uniquement dans expr2. Mais ce nom n'est associé à rien dans expr1 ou ailleurs (à moins qu'il y ait ailleurs une autre définition associant name à une valeur).

Reprenons un de nos tout premiers programmes, et décortiquons-le comme le fait le compilateur :

let x = 238 in
print_int x;
print_string " ";
print_int (x * x)

Explications. Le nom associé à une valeur est x. La valeur associé à ce nom est 238. L'expression dans laquelle le nom x est associé à la valeur 238 est l'expression :

print_int x; print_string " "; print_int (x * x)

C'est une juxtaposition de trois expressions de type unit, donc une expression de type unit. L'ensemble du programme est donc aussi une expression de type unit.

Remarque : le programme peut être terminé par un point-virgule si on veut, mais il n'y en a pas besoin puisqu'il n'y a rien après.

Décortiquons maintenant un autre programme, avec deux let :

let x = read_int() in
let y = read_int() in
print_int (x + y)

Il y a deux déclarations. Dans la première, on associe le nom x à une première valeur donnée par l'utilisateur, et ce à l'intérieur de l'expression :

let y = read_int() in
print_int (x + y)

Dans cette seconde déclaration, on associe le nom y à une seconde valeur donnée par l'utilisateur, et ce à l'intérieur de l'expression :

print_int (x + y)

Cette dernière expression est de type unit. Donc

let y = read_int() in
print_int (x + y)

est aussi de type unit. Et par conséquent :

let x = read_int() in
let y = read_int() in
print_int (x + y)

est une expression de type unit, qui forme la totalité du programme.

Regardons alors ce qui se passe lors de l'évaluation. D'abord on donne une valeur à x, par exemple 5. Ainsi le code est équivalent à :

let y = read_int() in
print_int (5 + y)

Ensuite on donne une valeur à y, par exemple 8. Il reste :

print_int (5 + 8)

Ce code affiche 13, qui est le résultat attendu du programme.

Dans certains cas, il peut y avoir une ambiguïté sur la manière de lire les expressions. Il y a ainsi deux façons d lire :

let x = read_int() in
print_int x;
let y = read_int() in
print_int y;

La première méthode, comme déclaration dans un bloc :

let x = read_int() in
begin print_int x; let y = read_int() in print_int y end

La seconde méthode, comme juxtaposition de deux blocs contenant chacun une déclaration :

begin let x = read_int() in print_int x end;
begin let y = read_int() in print_int y end

Dans une telle situation, le compilateur a une méthode pour choisir un des deux découpages possibles. Mais comme on n'a pas besoin de connaître cette méthode en détails, on n'en dira pas plus. De toutes façons, le résultat dans les deux cas est équivalent.

Pensez à vous inscrire pour valider les cours et résoudre les exercices.