Cher lecteur.
En programmation, nous sommes souvent amenés à utiliser des langages variés pour accomplir des fonctions précises. Certains, sont adaptés à des tâches particulières et d'autres non.
En effet, il n'est pas envisageable de créer un site web avec le langage Python, ni de créer un jeu vidéo (comme avec Pygame même si la ressource est limitée...) avec le langage HTML.
Néanmoins, nous pouvons "différencier" chaque langage par son paradigme... c'est-à-dire la manière dont on l'utilise pour traiter un problème.
1. Le paradigme impératif.
2. Le paradigme fonctionnel.
3. Le paradigme objet.
4. Le paradigme logique.
Dans le cadre de notre travail, nous pourrons dès alors nous demander lequel est approprié en sciences. En vérité, ils sont TOUS appropriés : chacun permet de mener une analyse.
Les langages impératifs permettent une modélisation formée sur des données, les langages fonctionnels sont pratiques pour les calculs rapides et réels, à faire sur grande échelle. Ce que l'on retrouve, pour l'analyse finie d'éléments où l'on doit faire un grand nombre d'opérations. L'intérêt est donc d'alléger les programmes, en évitant de changer "trop" une variable, et en standardisant une opération.
Les langages objets sont utiles, pour la mise en place de programmes informatiques avancés fondés notamment sur des systèmes pour les intelligences artificielles, où on fait interagir les neurones. De plus, ils permettent de traiter des grandes quantités de données de façon interactive à la manière d'une toile qui est tissée à partir de celles-ci.
Dès alors, dans le cadre scientifique, nous serons néanmoins amenés dans le cadre d'études de système si l'on s'intéresse à ceux-ci dans un cadre fermé... à considérer tous les paramètres qui peuvent affecter ceux-ci. On peut alors s'intéresser à une partie du système et l'étudier, or pour ce faire... il est requis de passer par un grand nombre d'opérations que l'on peut faciliter intelligemment par le biais de l'OCaml. C'est dans ce contexte, que nous verrons l'utilisation de l'OCaml comme étant une véritable clé pour traiter ces sujets.
En effet, l'OCaml est un langage fonctionnel né dans les années 1980 par l'équipe Formelle de l'Inria et de l'automate abstrait CAM de Guy Cousineau. Le langage s'est défini à travers son appartenance aux langage ML (meta-language, à ne pas confondre avec machine learning). Il s'agit d'une grande famille de langages variés, conçus pour décrire des langages de programmation. Dans "OCaml", l'appellation Caml signifie "Categorical Abstract Machine Langage", et l'intérêt de ce langage est donc de prendre un nouveau point de vue sur la programmation. En effet, par ce langage, "fonctionnel" nous pouvons accomplir des tâches sur les fonctions et les exploiter.
Dans ce contexte, nous chercherons à acquérir les bases de l'OCaml et à comprendre en profondeur ses propriétés et ce à quoi il fait référence à travers ses nombreuses propriétés. Cela, dans le but d'apprendre et d'obtenir un nouvel outil scientifique utile. L'OCaml est aussi utilisé l'industrie, notamment chez de grandes entreprises comme Facebook, Microsoft, Bloomberg et Jane Street.
Le langage Ocaml est souvent représentée par un châmeau, notamment car châmeau en anglais s'écrit camel faisant penser à l'animal :
Le langage OCaml, étant un langage fonctionnel, ne peut PAS voir ses variables changer. Il faut donc, se rappeler de cela durant toute l'étude de celui-ci. Ses variables sont immutables, permanentes.
Ainsi, commençons.
Tout d'abord, pour définir un objet dans le langage OCaml nous pouvons procéder ainsi :
On va remarquer tout d'abord, que chaque ligne de code en OCaml se termine par deux points-virgules. Il s'agit d'une convention, à l'instar du C où tout se termine par un point-virgule qui permettra à la machine de comprendre que l'instruction se termine ici.
Plus largement, on dit "Let" de manière à dire en français "Soit". On retrouve ainsi, une définition semblable à celle d'un objet mathématique.
On précise aussi le type d'objet que l'on définit suite à l'instruction "Let", cela avant de nommer l'objet et de lui donner sa forme en le définissant.
Nous pouvons procéder ainsi, si nous voulons préciser le type de l'objet.
En effet, l'un des avantages du langage OCaml est qu'il possède l'inférence du type.
L'inférence du type, permet au langage de déduire "automatiquement" le type de l'objet définit à partir de sa forme. Imaginez-vous un enfant, qui regarde une forme géométrique et la reconnaît, il peut la classer soit en "triangle", "carré" ou "cercle". Le langage OCaml fait la même chose, il regarde ce que vous définissez et de là trouve, de quel type d'objet il s'agit.
Affichons les !
Il en vas, que nous pouvons suivre l'utilisation du "print" classique en OCaml.
On a ainsi :
Or, cet affichage ne nous donne pas les variables "unes-à-unes" mais concaténées, nous avons donc un problème.
Il est possible d'obtenir un affichage correct à l'aide de l'utilisation de "print", or il faudra afin de distinguer chaque objet les séparer par un saut de ligne, que l'on fera à l'aide de "print_newline()".
On a, ainsi :
Les opérations d'addition, de multiplication et de division ainsi que de division entières sont définies respectivement par les opérateurs + * et / pour about au résultat.
Or, pour les entiers flottants nous aurons besoin de rajouter un point à ces opérateurs :
Ainsi, l'on obtient comme résultat, en changeant le "print_int" en "print_float".
De plus, nous pouvons considérer les opérateurs logiques : ils servent à établir des faits logiques "faux" ou "vrai". Par exemple, prenons cette déclaration : "1=2", elle est fausse et donc si on devait lui donner une valeur de "vérité" ce serait 0. De même, l'on "1=1" est vrai et donc vaudrait 1.
Ces valeurs de "vrai" et de "faux" sont le 0 et ou le 1 posés ici par l'analogie !
On distingue ainsi, des éléments selon leur vérité.
On pose alors l'opérateur && (et), l'opérateur || (ou) et l'opérateur not (non). Ils seront utiles, notamment afin de poser des conditions pour les fonctions.
Les opérateurs pour comparer sont les mêmes qu'en Python, à l'exception de celui d'égalité qui est = et < > qui vérifie si deux éléments sont différents. Il est à noter, que ces opérateurs ont tous une valeur "vrai" ou "faux" et permettent de distinguer des conditions, que l'on utilisera pour forger un programme pouvant accomplir une tâche.
J'invite le lecteur, à regarder les tableaux de vérités et à approfondir la logique, ici présentée de façon rudimentaire afin de trouver des informations pertinentes.
B. Les fonctions et la récursivité !
Voici, la partie la plus intéressante du langage OCaml en mon avis : les fonctions.
En effet, nous allons voir que les fonctions ne se limitent pas simplement à des fonctions semblables à celles en Python, mais que nous pouvons définir certains cas et explorer des opérations plus complexes, ainsi que des manipulations uniques !
Prenons un cas simple, imaginez vous vouloir créer une fonction telle que le carré du nombre entré soit renvoyé.
Il en découle, que l'on vas reprendre le principe de définition d'un objet et utiliser le "let".
La structure d'une fonction sera la suivante :
let + (nom) + (entrée) = (opération);;
En respectant cela :
On a :
let carre a = a*a;;
On peut vérifier ce programme, en OCaml.
On peut, reprendre ce principe pour toute autre fonction.
Or, cela devient très rapidement limitant.
Dès alors, utilisons les opérateurs logiques !
Imaginons-nous vouloir fabriquer une fonction qui vérifie si un entier est supérieur à un autre, si oui on obtient 0 et si non on obtient 1.
Alors, on aura :
let comparaison a b : if a>b then 1 else 0
Testons la !
Nous avons bien le résultat voulu, car 9 est supérieur à 3.
Ainsi, nous observons déjà comment l'on peut structurer une fonction à partir des opérateurs logiques.
De plus, il est possible de s'imaginer que l'on peut étendre cela à tout les opérateurs logiques pour obtenir le résultat désiré.
Remarque : On a fait ici un commentaire, que l'on a formé entre des crochets contenant des étoiles. Il en découle que les commentaires portent une importance fondamentale. Ils permettent de comprendre ce que fait votre code, et de préciser des nuances potentielles quant à votre approche à un problème.
Je laisse, en exercice au lecteur ces programmes à réaliser :
1 - Un programme, qui fais la somme de deux nombres et si celle-ci est supérieure à 3 alors l'on obtient 1 et sinon 0.
2 - Un programme, où si le produit de deux nombres est positif et supérieur à 10 on renvoie 1, sinon on renvoie 0.
Indication : utiliser &&.
Vous pouvez utiliser onecompiler en ligne pour les faire.
Or, nous pouvons nous intéresser à un cas spécifique : comment calculer une fonction où le résultat spécifique n'est pas immédiatement facile à obtenir. Par exemple, la fonction factorielle..
Rappel : la factorielle, écrite n! correspond au produit de tous les entiers allant de 1 à n. Cette fonction a une importance majeure en mathématiques, et en combinatoire notamment en dénombrement.
Dans ce contexte là, il ne peut pas forcément sembler "évident" de trouver la valeur de la factorielle... à moins que l'on utilise une "boucle".
Or, on se souvient que cela ne marche pas... en vue de l'immutabilité des variables.
Dès alors, il faut introduire la notion de récursivité.
La notion de récursivité, peut être vue comme une descente d'escaliers... la même personne est sur l'escalier en le descendant, mais elle descend juste d'une marche. Elle reste la même, mais descend juste d'une marche jusqu'à la dernière.
Ici, imaginons que la personne est aussi définie par la marche sur laquelle elle est.
Alors, notre objectif sera de "descendre" jusqu'à obtenir le résultat voulu, en faisant que la personne descende d'une marche par elle-même.
C'est cette idée, de la personne qui descend par "elle-même" qui définit la récurrence.
La factorielle est définie comme le produit de 1 à n des entiers naturels. On a ainsi : n!=n*(n-1)!, il s'agit d'une réécriture.
Ainsi, l'on peut adapter cette notion avec la factorielle que l'on vas définir. On posera rec après let afin de faire comprendre qu'il s'agit d'une fonction récursive.
On pose ainsi :
let rec factorielle n =
if n=1 do 0 not then n*factorielle(n-1);;
Ce principe, s'illustre ainsi dans le fait que l'on peut utiliser une fonction dans une autre.
C'est-à-dire que l'on peut descendre une marche en prenant un pas, par soi-même.
En répétant le processus, on obtient bien le résultat voulu pour n=9 :
La récursivité, est fondamentale : elle permet, à grande échelle en utilisant une fonction en elle-même d'accomplir des opérations complexes. Imaginez vous, certaines opérations mathématiques où l'on aurait besoin d'une fonction qui est définie par elle-même comme une suite... c'est là où le principe s'illustre et est utile.
On va, néanmoins introduire d'autres notions, telles que celle de "boucle".
Gardons des exemples simples, imaginons que je veux faire la somme des 100 premiers entiers.
Comment faire ?
Tout d'abord, on vas définir les opérations utilisées pour la "boucle".
Il faut savoir que l'on peut un peu "tricher" sur l'immutabilité en OCaml.
En effet, cela peut paraître surprenant mais il existe bel et bien un moyen de surmonter cette limite que le langage nous impose.
Il s'agit de l'utilisation de "let (variable) = ref (donnée)", un opérateur qui stocke une variable dans une place de la mémoire que l'on peut muter.
Pour la lire, on peut utiliser "!(variable)" afin de l'utiliser... et pour la modifier on peut écrire "(donnée) := (nouvelle donnée)".
Oui, on a l'impression de faire quelque chose d'interdit... mais la programmation c'est aussi dès fois "tricher" quand quelque chose devient trop dur (mais tricher "logiquement", je déconseille formellement de tricher en vue de la vacuité du geste).
On peut ainsi, obtenir ce que l'on souhaite en exploitant cela ! Un exemple est une autre formule pour la factorielle.
La fonction factorielle, ici définit une valeur "ref" qui est mutable soit 1. Ensuite, de 2 à n, elle redéfinit a en rappelant la valeur de a avec !a pour la lire. Elle multiplie celle-ci avec celle des entiers de 2 à n. On obtient donc une expression équivalente.
Le résultat, correspond :
Or, allons ENCORE plus loin.
Imaginons-nous vouloir tester une suite afin de déterminer à quel rang une valeur sera atteinte ou surpassée...
Exemple avec une suite U_n : U_n=(1,2^n)*U_0
Alors, l'on veut savoir quand sa valeur dépassera 10.
On doit, utiliser la boucle "tant que" soit la boucle "while". On pose U_0=1 et on définit sur N* les prochains termes de la suite que l'on étudie !
Testons...
On trouve que au rang 20, on a U_n>30.
Par vérification, on a : 1,2*19 =~ 31.94 >30.
De plus, 1,2^18 =~ 26.623
Notre programme, a donc fait ce qui lui était demandé ! Ne pas oublier de mettre "done;;" à la fin de chaque boucle formée par un while ou un for.
Nous avons donc un arsenal d'outils, qui nous sont ainsi donnés en OCaml pour faire des fonctions : or, le plus efficace est un dont nous n'avons pas encore parlé.
Il s'agit du pattern-matching.
Le nom, doit déjà vous donner une idée du concept qui vous est proposé. Le "pattern-matching" peut se définir comme étant un procédé, que l'on incorpore à une fonction en OCaml pour guider selon les cas son comportement.
Cet outil, est très puissant car il simplifie certaines tâches et permet de simplifier plusieurs programmes en faisant en sortes, que l'on écarte certains cas.
Imaginons-nous ceci, nous voulons fabriquer un programme qui si l'on entre 1, 2, 3 et 4 nous renvoie respectivement 2, 3, 4, 1 pour chaque entrée.
L'idée, serait de passer par des if mais même là le problème semble compliqué à traiter, à moins de chercher un moyen assez difficile de relier les entrées à leurs sorties algébriquement.
Cherchons plus simple !
Associons à chaque entrée possible, une sortie respective.
Pour cela, on va utiliser "match (nom) with" afin de faire le lien avec la fonction, et la définir également par récurrence :
Ainsi.
let choix a=
match a with
| 1 -> 2
| 2 -> 3
| 3 -> 4
| 4 -> 1;;
On observe, que avec le pattern-matching, l'on associe à chaque nombre par le biais de "->", une valeur.
Testons notre programme pour a=3 :
Le pattern-matching est très puissant, on peut l'associer à des listes voire à d'autres éléments afin d'obtenir des résultats précis mais également de le combiner avec d'autres éléments. L'intérêt, est donc de faciliter le travail quand il faut créer des fonctions souvent avancées !
Il se peut, que vous ayez une erreur si vous n'avez pas associé à tous les cas possibles liés à l'entrée un résultat, mais ça n'empêche pas le programme de fonctionner.
La vraie puissance du pattern-matching repose dans le fait que l'on peut l'utiliser avec des cellules mutables et à partir de là réellement faire des manipulations avancées.
On a ainsi, de nouvelles possibilités que des langages impératifs comme le Python n'offrent pas.
C. Droit devant, vers les polymorphismes !
Maintenant, que nous avons vus les bases, intéressons-nous à d'autres opérations avancées.
Imaginez-vous une fonction, qui fais la somme de deux nombres. Imaginons que nous voulons une fonction, qui concatène deux caractères. Il suffirait, de reprendre la première et de faire des modifications légères.
On retrouve ce problème assez souvent, en effet : on peut utiliser une fonction traitant un objet donné, et en faire une autre qui est très proche mais qui utilise un type différent en entrée.
Exhaustivement, il n'est pas avantageux de réécrire la fonction : cela nous fait perdre du temps...
Dès alors, le langage OCaml offre une possibilité : celle de l'usage du polymorphisme.
En effet, il est possible de faire en sorte que le langage ne traite qu'une entrée dans une fonction peu importe son type, et la traite de la même manière que si on avait fait une fonction pour chaque type !
On parle alors de polymorphisme. Nous pouvons penser à "poly" qui signifie plusieurs, et "morphismes" qui fait penser à transformation et aux formes que peut prendre un objet. Il en découle ainsi que, le polymorphisme peut se définir comme étant l'application d'une fonction unique en programmation à un objet de type quelconque.
Tout d'abord, pour préciser que le type de l'objet peut être "quelconque" il faut écrire un "type générique" :
'(nom)
Ces "types génériques" permettent de satisfaire les propriétés de plusieurs types en même temps lorsqu'on réalise une opération. On peut s'imaginer que avant, l'on avait que une fonction qui avait une seule porte d'entrée vers une seule porte de sortie. L'idée, avec les types génériques, est d'avoir autant de portes d'entrées que de portes de sorties pour une seule fonction.
Nous pouvons utiliser des fonctions "polymorphes" pour les manipuler.
Un exemple serait tout simplement la fonction qui renvoie son propre résultat.
On nomme celle-ci la fonction "neutre".
let neutre (x : 'n) : 'n= x;;
L'écriture, est telle que :
let + (nom) + (entrée : '(nom)) + : + '(nom) + = entrée;;
On précise ainsi, que l'entrée peut correspondre à un type générique.
Remarque importante : on peut ajouter plusieurs paramètres à une fonction, à condition de les mettre entre parenthèses avant le double point.
Je vous conseille, de tenter de faire vos propres programmes avec toutes ces informations. En effet, il est possible de faire pas mal de choses.
Essayez, avant tout de combiner.
J'insiste, l'exercice surpasse le reste sachant que une compréhension du cours peut être "utile" mais le cerveau apprend en répétant, et en développant des "raccourcis".
On peut employer la métaphore des neurones, on forge les ponts entre eux mais pas avec une seule brique...
D. Les listes et les types avancés !
Il faut savoir, que en OCaml les listes sont définies par type générique, c'est-à-dire que l'on peut les écrire ainsi :
'(nom) liste
On aura donc, des listes soit d'entiers en fonction du type de '(nom) soit de booléens, voire même de listes...
Cela, nous offre une myriade de possibilités et une opportunité d'utiliser les listes dans une programmation plus soucieuse.
Pour ajouter un nouvel élément à la liste, on procède ainsi :
(élément) :: liste
Il faut savoir, que l'avantage des listes est l'organisation d'éléments de types uniformes ou d'éléments.
Voici des exemples de liste :
List.map (fonction anonyme "fun", un exemple sera donné plus tard dans l'article) [ (élements de la liste, séparés par un point virgule) ] -> Crée une liste.
[ ] : la liste vide..
[ 1; 2; 3 ] : la liste représentée par int liste.
[ True ; False ] : la liste des booléens.
Plusieurs opération s'appliquent au liste, on pourra les lister (jeu de mot non intentionnel).
List.length (liste) -> renvoie la longueur d'une liste.
List.hd (liste) (tête de la liste "head") -> renvoie le premier élément de la liste.
List.tl (liste) -> renvoie tous les éléments sauf le premier.
liste @ liste2 -> combine les listes.
List.nth (liste) (index) -> renvoie l'élément n-ième de la liste, n compris entre 0 et (liste.length-1). Ne fonctionne pas si la liste est la liste vide.
List.filter (liste) (fonction anonyme) -> Nous pouvons filtrer, selon des critères les éléments de la liste.
List.sort compare (liste) -> organise dans l'ordre alphabétique ou croissant les éléments.
Il est nécessaire, de signaler que les listes sont toutes faites d'éléments de même types.
Ces opérations sont utiles, mais demandent de la pratique avancée.
On aura d'abord les types sommes tels que :
type Blog =
Mathématiques : (type)
| Sciences Physiques : (type)
| Informatique : (type);;
On aura aussi les types produits... qui peuvent s'écrire tels que :
type (Nom) = (type) * (type);;
Soit un tuple.
L'intérêt, est ici que en fonction du type lié à Blog, on aura un résultat différent et cela permet d'aborder les cas différents de façon uniforme.
Il est à noter qu'il y'a une différence entre type et "let".
On peut observer ça avec cet exemple de type produit :
let livre : (string*int) = ("George Orwell", 1984);;
Il en découle, que l'organisation par tuples est particulièrement utile dans certains programmes où l'on souhaiterait organiser des données. On peut s'imaginer, prendre en argument un élément d'une liste et un deuxième d'une autre.
On peut expérimenter avec, de façon assez efficace quand ça en vient à traiter des données...
E. La fonction fun !
En OCaml, il existe une fonction semblable à celle "lambda" en Python... la fonction fun.
Elle permet, de faire rapidement des opérations "anonymes" sur un objet.
Par exemple :
Gauss.map (fun x -> (x*(x+1))/2) = [100 ; 200 ; 300];;
Renvoie pour la liste Gauss : [ 5050 ; 20100, 45150 ].
On peut donc, appliquer ces fonctions "discrètes" à des entrées, ce qui est très puissant.
En effet, cela nous évite ici d'avoir à faire l'opération pour chaque entier... et on peut s'imaginer que dans certains cas on peut vouloir obtenir la période par analyse physique. Ensuite, on pourrait appliquer l'opération inverse pour avoir la fréquence dans la liste avec la fonction fun promptement.
Il y'a plusieurs cas, où leur usage est quintessentiel, à la bonne réalisation d'une tâche pratique.
let a = ( fun x -> x*x) 5 a ;;
Si l'on imprime a :
a=25
On applique l'opération fun à un entier, que l'on place dans a.
En parallèle, on peut aussi "filtrer" une liste avec.
Càd, que nous pouvons créer une condition qui filtre.
Imaginons cette liste :
let Premiers = [ 2 ; 3 ; 5 ; 7];;
On veut savoir quel premier est pair, et lequel ne l'est pas.
On peut alors, demander l'aide de la fonction anonyme.
let pair = list.filter (fun x mod 2=0) Premiers;;
pair sera alors le seul nombre pair induit dans la liste, 2. Il s'agira du singleton 2 si on considère comme une liste les premiers filtrés.
F. Les arbres binaires : vers des structures avancées.
L'une des parties les plus complexes à étudier, est celle des arbres.
En effet, on peut vouloir organiser en arbres les données.
Imaginons-nous les nombres tels une montagne.
Cette montagne, aurait un sommet, un nombre ultime.
Imaginons-nous classer les nombres inférieurs à ce nombre de façon décroissante.
On aura alors un chemin vers le nombre.
Néanmoins, on peut créer deux chemins si l'on en crée un pour les pairs inférieurs et un pour les impairs.
Dès alors, comment représenter des structures semblables... là où l'on voudrait une sorte de réseaux.
Il faut savoir, que dans le cadre par exemple des IA les structures d'arbres sont essentielles car elles permettent de relier les données et les poids des perceptrons.
Ainsi, l'on voudra comprendre "comment" créer un arbre.
On considère un arbre comme des Noeux liés entre eux.
La structure d'un Noeux est la suivante :
let Noeud = ("Analyse", "Trigonométrie", Leaf);;
Ici, l'on définit un "Noeud" en précisant ses éléments.
Plus simplement, un arbre a :
-Une racine (le premier noeud qui contient ceux qui vont suivre).
-Branches (les noeuds suivants).
-Des feuilles (qui marquent l'arrêt d'un nœud, en indiquant la fin de la descente).
Si l'on voudrait représenter notre exemple d'avant, on écrirait :
let montagne =
Node("10",
Node("9", Node("7", Node("5", Node("3", Node("1", Leaf, Leaf), Leaf), Leaf), Leaf),
Node ("8", Node("6", Node("4", Node("2", Leaf, Leaf), Leaf), Leaf), Leaf);;
Cette écriture, est terrifiante. Elle paraît incompréhensible, mais en réalité elle est assez intuitive quand on comprend...
La première ligne, crée ici la Node initiale qui contiendra les autres...
La première est au sommet de l'arbre, les autres en dessous.
Dans la ligne on fais en sorte que la node 9 : Node("9", Node("7", Node("5", Node("3", Node("1", Leaf, Leaf), Leaf), Leaf), Leaf), contienne une node qui fera descendre encore d'un rang et on répète jusqu'à 1 avant de fermer les paranthèses. Cela, sans oublier les feuilles qui viennent marquer qu'il n'y a pas de "descendant" de l'autre côté.
Les leafs permettent de délimiter le fait que chaque noeud n'a qu'un "descendant" ! Le concept, est assez ardu à comprendre mais il est nécessaire d'avoir des notions mentales d'imbrication, pour l'appréhender.
Ainsi, on crée une sorte de "descente", que l'on refait pour : Node ("8", Node("6", Node("4", Node("2", Leaf))));;
Ce qui nous donne cette visualisation de l'arbre...
Or, il y'a un problème... on aurait voulu que les branches soient bel et bien séparés. Sachant que les nœuds suivant le premier dans l'arbre initial... contiennent chacun des noeuds. On observe quelque chose, les feuilles ont la même structure. En effet, elles sont placées de la même manière.
On aura bien le noeud "8" et le noeud "9" qui vont se distinguer, car placés séparément... or on comprend, que il aurait dès alors fallu que l'on place les feuilles dans l'ordre opposé car leurs positions indique vers quel direction on descend.
Le premier élément du nœud, simplement indique la racine.
Les suivants, indiquent de gauche à droite, l'ordre des descendants.
Ainsi, on corrige :
let montagne =
Node("10",
Node("9", Node("7", Node("5", Node("3", Node("1", Leaf, Leaf), Leaf), Leaf), Leaf),
Node ("8", Leaf, Node("6", Leaf, Node( "4", Leaf, Node("2", Leaf, Leaf))));;
Ce qui nous donne :
Les arbres binaires sont particulièrement utiles, ils peuvent contenir n'importe quoi et on peut même aller plus loin dans leurs utilisations afin de classer des données.
En effet, on peut contenir beaucoup d'éléments dans un arbre binaire et presque les utiliser comme chemins pour trouver des données. Ils agissent ainsi, comme de grandes bases de données visant à structurer un ensemble quelconque.
Leur avantage, est tant bien que la recherche d'un élément est de complexité O(log n) si l'on crée des arbres binaires de "recherche" adaptés et équilibrés.
G. Conclusion.
L'OCaml constitue, un outil puissant en programmation : langage de pointe il permet de concrétiser de nombreuses tâches et adopte un comportement adapté dans plusieurs domaines. Notre analyse de ses propriétés converge bien vers cette idée, le langage par son immutabilité et ses règles permet de distinguer les données de manière plus précise que les langages impératifs usuels. De plus les nombreuses manières de l'utiliser de façon unique, de créer un code synthétique et court s'additionnent à son attractivité.
Il est notamment possible de manipuler des objets que nous ne sommes pas habitués à employer, voire avec le pattern-matching d'anticiper et de prévoir les problèmes dans un projet.
On peut ainsi avec une plus grande flexibilité, concision et efficacité pour coder dans les cas les plus adaptés pour mettre en forme la construction de programmes.
On a de plus un certain contrôle que l'on ressent plus grand en l'employant, en vue de ses règles et de la différenciation que chaque élément possède et qui permet de les distinguer.
Nous n'avons abordé qu'une facette de ce langage. Il existe en effet une myriade d'éléments divers, ayant chacun un rôle spécifique mais une fonction efficace, permettant d'employer différemment certains outils.
Le lecteur est invité après une telle lecture à se renseigner, apprendre et s'exercer sur celui-ci. Cela, non seulement en vue de son importance pour sa propre culture générale mais également pour découvrir une nouvelle manière de voir la programmation...
Le travail du scientifique est d'étendre son territoire intellectuel, et de s'aventurer dans des confins toujours plus étroits pour apprendre sur le monde autour de lui. Or, il s'agit aussi d'accepter le défi, le défi d'apprendre, qui par le temps et la discipline pousse à l'excellence.
Cordialement,
DAOUADI Zine-Eddine.
Cet article est mis à votre disposition sous la licence Creative Commons Attribution 4.0 International (CC BY 4.0).
Commentaires
Enregistrer un commentaire