Découverte d'OCAML

Dans ce petit article, je compte vous parler d'un langage de programmation assez particulier : (O)CAML. Tout d'abord, si j'ajoute une parenthèse devant la première lettre du sigle, c'est que je ne compte pas aborder toute la partie OBJET de langage, ni la partie impérative. (N'espérez pas voir une seul structure conditionnel[if] ou itérative[while/for] dans ce texte.

Propositions : définitions et expressions :

Tout comme en mathématique chaque phrase est une proposition, en ocaml chaque "ligne" est une proposition. Elle commence avec le premier caractère et se termine avec le symbole ';;'

Il y a deux type de propositions :

  1. Les expressions : De la forme \(3*7\) ou encore \(\sqrt[7]{\frac{2^9}{3^{43}}}\)
  2. Les définitions : De la forme \(q := \sqrt{2}\) qui déclare q comme un alias de \(\sqrt{2}\)

En ocaml, la syntaxe est la suivante :

  1. Pour un expression :
    3 * 7;;

    ou encore (pour\(e^{log(\frac{exp(log(2) * 9)}{exp (log(3) * 43)}) / 7}\))

    exp (log (exp(log 2. *. 9.) /. exp(log 3. *. 43.) ) /. 7.);;
  2. Enfin, une définition :
    let q = sqrt 2;;

L'aspect fonctionnel

Mais comment un tel langage peut-il exister, et surtout, être 'utilisable' sans pour autant faire usage des structures des langages impératif comme le C ou le Python? (Je m'adresse bien-sur ici a ceux qui n'ont pas encore été initier aux joies des langages fonctionnel)

Tout d'abord, définissons ce qu'est un langage fonctionnel. Par analogie à un langage objet où les éléments sont des objets(Dans certains cas, TOUS les éléments), dans un langage fonctionnel, les éléments sont des fonctions, au sens mathématique du terme.

Si l'on considère la fonction suivante : \(\begin{array}{ccccc} f & : & \mathbb{Z} & \to & \mathbb{Z}^2 \\ & & x & \mapsto & (x, x) \end{array}\)

C'est la fonction qui associe à un entier \(x\) son couple situer sur sa diagonal(injection diagonal) \((x,x) \in \Delta\mathbb{Z} \subset \mathbb{Z}^2\)

Il est alors très aisé de définir f en ocaml :

let f x = (x, x);

Cette fonction est de type int -> int * int, c'est a dire qu'elle prend un entier \(int \approx \mathbb{Z}\) et retourne un couple \((x, y) \in \mathbb{Z} \times \mathbb{Z}\)

On peut bien-sur définir des fonctions de fonction. Observons la fonction "composition"(rond) : \(\begin{array}{ccccc} o & : & (\mathbb{B} \Rightarrow \mathbb{C} ) \times (\mathbb{A} \Rightarrow \mathbb{B} ) & \longrightarrow & (\mathbb{A} \to \mathbb{C} ) \\ & & (f, g) & \longmapsto & (x \mapsto f(g(x)) \end{array}\)
En OCAML, on aurais :

let o f g = f g;;
(* val o : ('a -> 'b) -> 'a -> 'b = <fun> *)

Les filtres

Je vous ai parler un peut plus tôt de l'absence de structures conditionnel et itératif, je vais ici vous exposer les solutions a votre disposition.

Commençons avec les blocs conditionnel. En OCAML, nous disposons d'une fonction-alitée très intéressante : les filtres.
Si l'on considère la fonction continue suivante : \(\begin{array}{ccccc} f & : & \mathbb{Z} & \to & \mathbb{Z} \\ & & x & \mapsto &
\left\{
\begin{array}{ccc}
x < 0 & \Rightarrow & -2x \\ x \ge 0 & \Rightarrow & x^2
\end{array}
\right.
\end{array}\)

On constate que l'on a bien deux condition, une première qui regroupe les cas des x négatif, et une deuxième qui s'intéresse aux x restant. En OCAML, on défini de la même façon :

let f = function
  | x when x < 0 -> -2 * x
  | x when x >= 0 -> x*x
  ;;

Ou bien, en omettant la deuxième condition et en considérons que la dernière condition matcheras pour "tous les cas restant"

let f = function
  | x when x < 0 -> -2 * x
  | x -> x*x
  ;;

Dans le cas où l'on a pas besoin de nommer x, on peut utiliser _. Ainsi, un dernier cas "_ -> smtg" correspondrais un peut à 'default' d'un switch.
On pourras aussi utiliser l'instruction match {variable} with {filtre}.

La récursivité terminal

Maintenant, considérons les itérations. Nous allons utiliser la récursivités terminal(tail-rec pour les intimes) affin de construire une boucle. La récursivitée terminal est une forme de récursivité ou l'unique action qui succède à l'appelle a la fonction récursive est un retour de valeur.

C'est plutôt élémentaire :

let rec aff_n = function
  | n when n < 1 -> ();
  | n -> print_endline (string_of_int n);
           aff_n (n - 1);;

() représente le type 'unit', c'est a dire que notre fonction ne retourne rien, tout comme print_endl. Nous obtenons une boucle de 10 à 1, et nous affichons les valeurs a l'écran.
Vous vous demandez alors, pourquoi de la tail rec et non une simple récursivités? La réponse est que l'interpréteur/compilateur optimise par une itération, ce qui est bien plus pratique pour nos machines qui on une mémoire limiter et ne peuvent supporter qu'un nombre restreins d'appelles récursif.

Pour conclure

OCAML est un langage fonctionnel, qui bon nombre d'aspects et concepts qui sont parfois inconnus des programmeurs aillant pratiquer uniquement des langages impératif, et constitue de par son existence une forme de culture dont il peut-être bon d'avoir connaissance. Si vous êtes curieux et désirez en savoir plus, voici quelques liens :

  1. Présentation d'ocaml
  2. Notes on OCaml
  3. Wikipedia
  4. Google
 

Bonjour,

Et bien, voici enfin la dernière release des specs de SFS, qui s'avère complète.

Plus qu'une simple specs, il s'agit d'une initiation a la structure d'un filesystem. Voici donc un bref résumé.

Inode Table

La table des inodes, où sont stocker toute les métadonnées des fichiers. On trouveras les valeurs particulières correspondant au mode ainsi que divers détaille sur son allocation etc...

Directories

La structure des dossiers, qui permettent de stocker des noms de fichier de taille variable et leur lien avec les inodes. La pluspart des filesystem sont construit sur le même modelé mais avec des noms de fichier a taille fixe.

Bref, sans plus attendre, voici le pdf : sfs.pdf

 

Bonsoir,

Comme promis, je publie ce soir ces quelques pages fraichement rédigées, agrémentées de deux schémas dont je suis plutôt fier, car représentatifs à la fois des données physiquement présentes (de simples bits), et de toute la portée sémantique qui leurs sont associé.

Un petit résumé sur les :

Inode/Block BitMap

Comme leurs nom l'indiquent, et on ne le répètera jamais assez, ce sont de simples cartes où chaque inode/block est représenté par un bit, actif ou inactif selon si l'inode ou le block est actif/inactif. Le bit n est associé au block/inode n. La forte quantité d'informations stockée dans peut d'espace permet une recherche rapide du prochain block/inode libre et est donc capital pour ne pas dégrader les performances.

SFS? Pas seulement!

Le concept de bitmap n'est pas nouveau, ni déprécié. Il était déjà employé dans minix et on le retrouve dans ext2/ext3 qui sont, pour ceux qui l'ignorerai encore, les systèmes de fichier de prédilection des distributions linux. (A vrais dire, a part cramfs, qui sert à monter une image en lecture d'une archive compressée, je ne connais pas de filesystem sans bitmap)

Je vous remet donc,  la seconde révision des specs de SFS : SFS.pdf

 

Bonsoir,

Cela fait maintenant plus d'un mois que je dissimule a la plupart d'entre vous, lecteur, le projet sur le quel je concentre toute mon attention depuis septembre.

Ce projet, est un projet plain de rebond, et je crains avoir a plusieurs reprise sous estimer le travaille nécessaire. Je suis toute fois heureux de vous annoncer que je suis en possession d'une implémentation qui, bien qu'instable, s'avère déjà partiellement fonctionnelle.

Je travaille actuellement sur un FileSystem et son implémentation dans le kernel Linux. Plus qu'un system de fichier, SFS seras avant tout un exemple claire et concis de filesystem, une sorte de journal de bord d'un voyageur qui tenta de traverser la jungle des filesystem et qui du affronter les plantes carnivores mangeuses d'inodes, les dangereux marais aux mémoires mouvantes, les lianes empoisonnés des dentries... bref, un périple qui pourrais presque donner lieux à une nouvelle.

Mais commençons par l'origine, le point de départ de ce projet, qui bien que n'aillant jusque la jamais été rédiger, est rester a chaque instant dans mon esprit :

Qu'est ce que SFS?

Simple File System : Simple système de fichier.

SFS se veut une implémentation d'un système de fichier élémentaire, baser sur une logique simple, une approche simpliste dans le but de réduire un maximum la complexité de l'implémentation pour une meilleur compréhension des couches d'abstraction du kernel linux.

Ainsi, il est de mon devoir, avant de publier une quelconque implémentation, d'établir les Specs de ce filesystem.

Le SuperBlock

Mais il y a tellement a dire, tellement de détaille indispensable a la bonne compréhension d'un filesystem. Tellement que je ne peux tout ecrire en une soirée. Alors, je vous propose le premiers chapitre des Specs de SFS : Le SuperBlock. C'est très certainement le plus court de tous les chapitres, étant donner que - selon moi - le superblock est au filesystem ce qu'est le préface à un roman.

Voici donc la première révision des specs de SFS : SFS.pdf

Je suis ouvert a tout commentaire :)

 

Bonjour,

Aujourd'hui, un petit article sans grande prétention pour évoquer une petite astuce que je dois à mon prof d'algo.

"Trouvez un algo de calcul de \( a^n\mid n\in\mathbb{N}, a\in\mathbb{R}\) avec un complexité inférieur à n"

En réalité, ce n'est pas si compliqué. b peut se décomposer aisément en puissances de deux via un masque binaire que l'on fait courir de droite à gauche, jusqu'à ce que la valeur de ce masque dépasse n. A chaque décalage du masque, on met au carré une même variable initialisée avec \(a\) et si \(masque \& b \not= 0\) on multiplie le résultat actuel par cette dernière.

Concrètement :

#define MATH_IF(r, a, b)        ((1 - (r)) * (b) + (r) * (a))
 
//Décompose b en puissances de deux
// ex: a^7 = a^(1+2+4) = a^(1*2^0 + 1*2^1 + 1*2^2)
//Decimal&lt;-&gt;Binaire 7dec = 111b
//On fait courir un masque de droite a gauche, et si le bit est actif,
// on ajouter la puissance de deux(donc un multiplie par a^mask)
int     npow(int a, int b)
{
  int   r;
  int   v;
  int   pw;
  int   mask;
 
  //n^0 = 1
  if (b &lt; 0)
    return 1;
 
  //pw = a^(2*k) and start at a^1 (k = 0)
  pw = a;
  //We will store result in v
  v = 1;
  //Binary mask, moving from right to left
  mask = 1;
  while (mask &lt;= b)
    {
      //Are there a bit set?
      r = (mask &amp; b) &amp;&amp; 1;
 
      //if r, v = v * pw
      v = MATH_IF(r, v * pw, v);
 
      //Move mask to left
      mask &lt;&lt;= 1;
      //pw = pw^2 (k++)
      pw = pw * pw;
    }
  return v;
}

Voila, c'est le genre de petites curiosités algorithmique amusantes.

© 2012 Zenol's Blog Suffusion theme by Sayontan Sinha