<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Billets dans la catégorie « Articles » — Projets inachevés</title><link href="http://blog.iuwt.fr/"/><id>http://blog.iuwt.fr/</id><updated>2010-02-27T18:33:59Z</updated>
<entry><title>Réussir Prologin</title><link href="http://blog.iuwt.fr/articles/preparation-prologin"/><id>http://blog.iuwt.fr/articles/preparation-prologin</id><updated>2009-10-25T17:17:55Z</updated><author><name>haveo</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Vous êtes un jeune, intéressé par l’informatique et vous venez de
découvrir l’existence d’un concours portant sur votre activité
préférée, Prologin. Plutôt sûr de vous-même vous décidez de
tenter votre chance. Mais bientôt, vous commencez à douter ; mais sur
quoi peut bien porter ce concours ? Vous parcourez le site officiel à
la recherche de renseignements, mais vous n’êtes pas satisfait. À
force d’acharnement, de contacts un peu louches et de recherches
Google douteuses vous arrivez sur un billet apparemment peu digne de
confiance qui prétend exposer les connaissances requises pour
Prologin.
</p><p><code>* &lt;----- vous êtes ici</code>
</p></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Vous êtes un jeune, intéressé par l’informatique et vous venez de
découvrir l’existence d’un concours portant sur votre activité
préférée, Prologin. Plutôt sûr de vous-même vous décidez de
tenter votre chance. Mais bientôt, vous commencez à douter ; mais sur
quoi peut bien porter ce concours ? Vous parcourez le site officiel à
la recherche de renseignements, mais vous n’êtes pas satisfait. À
force d’acharnement, de contacts un peu louches et de recherches
Google douteuses vous arrivez sur un billet apparemment peu digne de
confiance qui prétend exposer les connaissances requises pour
Prologin.
</p><p><code>* &lt;----- vous êtes ici</code>
</p><p>Vous êtes un jeune, intéressé par l’informatique et vous venez de
découvrir l’existence d’un concours portant sur votre activité
préférée, Prologin. Plutôt sûr de vous-même vous décidez de
tenter votre chance. Mais bientôt, vous commencez à douter ; mais sur
quoi peut bien porter ce concours ? Vous parcourez le site officiel à
la recherche de renseignements, mais vous n’êtes pas satisfait. À
force d’acharnement, de contacts un peu louches et de recherches
Google douteuses vous arrivez sur un billet apparemment peu digne de
confiance qui prétend exposer les connaissances requises pour
Prologin.
</p><p><code>* &lt;----- vous êtes ici</code>
</p><p>Comme vous le savez sûrement, le concours Prologin se déroule en trois
temps : qualification, demi-finale et finale. Il se trouve que la
qualification est tellement facile qu’elle est à la portée de tout un
chacun. La finale quand à elle est tellement spéciale que la seule
préparation efficace à une finale est … une autre finale. Lors de
votre première vous ferez probablement n’importe quoi, de préférence
en vous amusant et vous vous en contenterez.
</p><p>Gardez à l’esprit que ce billet n’est qu’un modeste résumé des
compétences que prologin requiert et en aucun cas une référence
exhaustive sur le sujet.
</p><h1>Rédaction
</h1><p>Premièrement, apprenez à présenter. Plus votre copie sera agréable à
lire moins le correcteur prendra de temps pour la lire. Le correcteur
est un bénévole, il corrige les copies parce que c’est un mal
nécessaire et probablement pas pour le plaisir. Moins il passera de
temps à tenter de déchiffrer ce que vous avez écrit plus il sera
content. Un code indenté, aéré, coloré et une écriture lisible seront
toujours récompensés.
</p><p>Deuxièmement, accompagnez votre code d’explications claires et
<em>concises</em>. Le correcteur n’est pas là pour le plaisir de déchiffrer
des codes obscurs. Vous ne risquez donc pas de lui gacher le plaisir
en lui expliquant comment votre algorithme fonctionne, au contraire.
</p><p>Un dicton de programmeur dit : Keep It Simple, Stupid (KISS). Dans la
mesure du possible, vous devriez chercher la solution la plus
simple. C’est celle qui a le plus de chance d’être juste et en plus
les correcteurs la comprendront aisément. Si toutefois vous séchez
n’hésitez pas à écrire ce à quoi vous avez pensé. Même si votre
algorithme est un peu alambiqué, qu’il ne marche pas tout le temps ou
qu’il ne vous satisfait pas en matière d’efficacité n’hésitez pas à
montrer au correcteur que vous avez fait l’effort de chercher. Qui
sait, peut-être que vous n’étiez pas si loin de la solution ?
</p><h1>Algorithmique
</h1><p>Lorsqu’on conçoit des algorithmes, on est tout particulièrement
intéressé par leur efficacité (à plusieurs points de vue) algorithmes,
par exemple le temps qu’ils mettent à s’executer.
</p><p>Ce qu’on nomme complexité d’un algorithme est la quantité de
ressources (temps ou mémoire principalement) consommée en fonction de
la taille de l’entrée. On utilise généralement des notations appelées
de Landau pour obtenir un aperçu du comportement pour de grandes
entrées (on parlera de comportement asymptotique) de cette
consommation. Ces notions sont abordées et expliquées en détail dans
le tutoriel "L’algorithmique pour l’apprenti programmeur" (suivez <a class="extern" href="http://www.siteduzero.com/tutoriel-3-51767-la-notion-de-complexite.html">le lien</a>).
</p><p>Une structure prépondérante en informatique est celle de graphe. On
peut se représenter un graphe par un ensemble de points (qu’on appelle
en fait noeuds) reliés par des traits (qu’on appelle en fait
arrêtes). Tous les problèmes où vous aurez affaire à un ensemble
d’éléments reliés les uns aux autres parlent en fait de graphes. On a
fréquemment besoin de chercher un élément dans un graphe. On utilise
pour cela des algorithmes de parcours de graphe comme BFS ou
DFS. Le tutoriel précédemment cité traite aussi de manière assez
intéressante ce sujet, <a class="extern" href="http://www.siteduzero.com/tutoriel-3-117382-arbres.html">ici</a> par exemple.
</p><p>Mais puisque toutes les questions ne portent pas sur les graphes
(heureusement) introduisons maintenant une autre technique, plus
universelle : la programmation dynamique. L’idée est très simple, on
conçoit un algorithme qui appelle une fonction récursive, pour
optimiser cet algorithme on enregistre les résultats des appels dans
une structure de données à temps d’accès constant de sorte que si
l’algorithme en vient à appeler deux fois la fonction avec les mêmes
valeurs il ne recalcule rien.
</p><p>Une application basique de ceci est le calcul des nombres de
Fibonacci. On définit ,<code>F_0 = 0</code> et <code>F_1 = 1</code> et <code>F_(n+2) = F_(n+1) + F_n</code>.
Si on utilise un algorithme récursif classique on peut montrer
que le calcul a une complexité exponentielle, c’est pas terrible. En
fait cela est du au fait que certains nombres de Fibonacci sont calculés
plusieurs fois. Par exemple, pour calculer F<sub>4</sub> on calcule F<sub>3</sub> et F<sub>2</sub>
puis pour calculer F<sub>3</sub> on calcule F<sub>2</sub> et F<sub>1.</sub> On a calculé F<sub>2</sub> au
moins deux fois. En utilisant la programmation dynamique on garantit
que chaque nombre n’est calculé qu’une fois et on peut donc montrer
que l’algorithme a une complexité linéaire. Un programme qui était
trop lent est devenu assez rapide.
</p><p>Une remarque assez importante, puisqu’on utilise une matrice pour
accéder aux résultats (ou à l’absence de résultats) de l’appel de la
fonction avec des paramètres donnés, ces paramètres doivent être
entiers pour pouvoir garantir un accès en temps constant et une
occupation mémoire décente. Si vous voulez utiliser la programmation
dynamique avec des chaines de caractère, des listes ou je ne sais quoi
de différent d’un entier (ou pire, si vous voulez les convertir en
entier), <em>vous vous trompez</em>. Toute la difficulté d’un certain nombre
de problèmes est justement de se ramener à des paramètres entiers
biens trouvés pour réduire le nombre de situations différentes.
</p><p>Exemple : Le problème du sac à dos, version facile.
Soit n objets différents disponibles à volonté de poids p<sub>i</sub> entier.
On a un sac à dos qui peut contenir un poids P, quel est le poids
maximum qu’on peut y mettre à l’aide de nos n objets.
</p><p>Solution : Ici, on remarque que le problème peut se ramener à résoudre
le problème dans le cas où on commence par mettre l’objet 1, puis dans
celui où on commence par mettre l’objet 2, etc.  Il suffira de prendre
le maximum de ces sous-cas qui couvrent bien tout l’éventail des
possibilités. L’astuce consiste à voir que deux sous-problèmes ont la
même réponse si le poids des objets qu’on considère comme étant de
toute façon dans le sac est le même dans les deux cas. Ainsi si on
considère le sous-problème où on commence par rentrer 1 puis 2 et
celui où on commence par rentrer 3 puis 4, il suffit que p<sub>1</sub> + p<sub>2</sub> =
p<sub>3</sub> + p<sub>4</sub> pour que ces deux problèmes aient la même réponse. Le
paramètre de notre fonction récursive sera donc la poids cumulé des
objets imposés a priori. On commencera par appeler la fonction avec ce
paramètre valant zéro. L’algorithme en lui-même est laissé en exercice
au lecteur.
</p><p>Ce sujet n’est malheureusement pas traité par "L’algorithmique pour
l’apprenti programmeur" (peut-être cela viendra un jour) ainsi je vous
recommande de vous tourner vers des ouvrages certes un peu moins
abordables mais bien plus complets comme l’excellente "Introduction à
l’algorithmique".
</p><p>Bien qu’il soit primordial de posséder un certain nombre de bases, il
est tout aussi important de savoir ce que vous ne devez pas
savoir. Les algorithmes avancés de pathfinding dont vous pourriez
avoir entendu parler (Djikstra, A*) ne vous seront d’aucune utilité de
par la complexité de leur implémentation (qui utilise par exemple des
files de priorité dont l’implémentation optimale est tout sauf
triviale). Ne pensez même pas à les utiliser, rabattez-vous sur DFS et
BFS.
</p><p>Je tiens également à vous prévenir de quelques questions classiques
des sujets d’écrit. La première question qui vous sera posée est
presque toujours la même, "Quelle structure de donnée allez-vous
utiliser pour stocker blah ?". Généralement les structures qu’on vous
demande sont simples. C’est la première question on n’allait pas vous
demander des structures exotiques. Une autre question "piège" c’est
l’évaluation du temps d’execution de votre algorithme. Vous venez de
calculer la complexité de votre algorithme (O( n<sup>2</sup> ), par exemple) et on
vous donne les paramètres (donc n) et les spécifications d’une machine
notamment (ce qui est le plus important) sa fréquence d’horloge, par
exemple 500 MHz (c’est-à-dire 500 x 10<sup>6</sup> instructions par seconde). Il
suffit donc de diviser le nombre d’instructions que vous avez à
calculer (n<sup>2</sup> dans ce cas là, il y a des constantes cachées mais on ne
vous demande qu’une estimation) par le nombre d’instructions executées
par secondes.
</p><p>Un petit résumé des ressources qui vous permettront d’approfondir le
sujet : <br/>
</p><ul><li><a class="extern" href="http://www.siteduzero.com/   tutoriel-3-51781-algorithmique-pour-l-apprenti-programmeur.html">"L'algorithmique pour l'apprenti
  programmeur"</a>,
tutoriel traitant de manière pédagogique mais malheureusement
incomplète des bases de l’algorithmique.
</li><li>"Introduction à l’algorithmique" de T.H. Cormen, ouvrage de
référence sur le sujet mais peut-être un peu trop touffu pour un
débutant.
</li><li>Prologin.org et France-IOI.org, deux sites proposant des exercices
pour mettre ces notions en pratique.
</li></ul><h1>Programmation
</h1><p>Il vous faudra aussi être au point niveau programmation et
connaissance de votre langage.
</p><p>On va vous fournir des codes pour gérer les entrées-sorties. Parfois
ils marcheront. Parfois non. Vous feriez donc mieux de connaitre les
primitives d’entrée-sortie de votre langage, au cas où.
</p><p>De manière plus générale, il est indispensable de s’exercer à la
pratique de son langage pour pouvoir d’une part éviter de faire trop
d’erreurs (qui peuvent faire perdre un peu de temps si ce sont des
erreurs de syntaxe voire beaucoup si elles sont plus subtiles) et
d’autre part les corriger pus rapidement. N’hésitez pas à demander de
l’aide (à des amis, sur des forums ou bien encore sur IRC) si vous ne
comprenez pas une erreur, vous aurez bien moins d’aide à disposition
le jour J !
</p><p>Là encore je vous conseille de traiter les exercices de Prologin.org
et France-IOI.org.
</p><p>Bonne chance !
</p></div></content></entry>
<entry><title>OneSwarm, un système peer-to-peer inspiré des réseaux sociaux.</title><link href="http://blog.iuwt.fr/articles/oneswarm"/><id>http://blog.iuwt.fr/articles/oneswarm</id><updated>2009-08-29T18:37:11Z</updated><author><name>asmanur</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"/></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p><a class="extern" href="http://oneswarm.cs.washington.edu/">OneSwarm</a> est un logiciel de
<em>peer-to-peer</em> dont le but est de garantir l’anonymat au contraire des
protocoles du type BitTorrent. Pour cela, l’information sur les
fichiers disponibles sur les réseaux n’est plus centralisée dans des
<em>trackers</em> mais est au contraire décentralisée. Avec BitTorrent, il
suffit de se connecter au <em>tracker</em> d’un fichier pour savoir qui le
possède ; avec OneSwarm cela n’est plus possible. Les transferts de
fichiers se font en effet qu’avec des pairs de confiance choisis par
l’utilisateur. On parle alors d’un système <em>friend-to-friend</em>. Les
avantages au niveau sécurité sont clairs : seuls les gens en qui vous
avez confiance peuvent voir vos données.
</p><p>Le <em>paper</em> en question décrivant l’architecture d’OneSwarm est
disponible <a class="extern" href="http://oneswarm.cs.washington.edu/f2f_tr.pdf">sur leur
site</a>.
</p><p>Deux points sont intéressants dans ce papier sans être trop
technique. D’une part, le <em>design</em> de OneSwarm est basé sur une analyse des
réseaux sociaux et <em>peer-to-peer</em> existants, d’autre part le moyen
démarrer (<em>bootstrap</em>) le réseau est lui aussi intéressant.
</p><h2>L’étude d’un réseau social, lastfm
</h2><p>Le <em>paper</em> commence par une analyse du réseau social
<a class="extern" href="http://www.last.fm">last.fm</a> et notamment étudie la structure du
graphe social. Par exemple, le chemin moyen pour aller d’un
utilisateur A à un utilisateur B passe par 7 utilisateurs ; tandis que
les deux utilisateurs les plus éloignés sont distants de 14
utilisateurs. Il est également déterminé la bande passante utilisé
pendant les deux semaines durant lesquelles les données ont été
récupérées : 4.66 TB. Ce qui ne fait qu’environ 4MB de données
transférées par utilisateur, cela est bien en-dessous de la capacité
des lignes ADSL actuelles.
</p><p>D’autres remarques sur le comportements des utilisateurs sont faites :
</p><ul><li>la majorité des chansons ne sont écoutées que par un seul
utilisateur ;
</li><li>malgré tout, ce sont les chansons les plus populaires qui
représentent la majorité du traffic ;
</li><li>il n’y a pas de corrélation évident entre <em>être intégré socialement</em>
et <em>écouter beaucoup de chansons</em>.
</li></ul><p>La conclusion tirée de cette étude est la suivante : <em>les réseaux p2p
ont largement plus de capacité totale que de demandes</em>. OneSwarm se
fixe donc pour objectif d’utiliser cet excès de capacité au service de l’anonymat.
</p><h2>Présentation de OneSwarm
</h2><p>OneSwarm n’est pas le premier système <em>friend-to-friend</em>, par exemple
<a class="extern" href="http://freenetproject.org">freenet</a> et son mode <em>DarkNet</em> proposait
un p2p privé de ce type. Un des problèmes des réseaux de ce type,
c’est le début : chaque utilisateur étant identifié par sa clé
publique, pour ajouter des utilisateurs il doit connaître leur clé
publique et les ajouter. C’est là la solution retenue par freenet :
l’ajout manuel de clés.
</p><p>OneSwarm propose une solution originale : au final avec un système
d’ami, on dote le système d’une structure de réseau social. Or les
utilisateurs sont déjà inscrits dans d’autres réseaux, et il est
fatiguant pour eux de toujours reconstruire ce graphe. Ainsi OneSwarm
permet d’utiliser un réseau déjà existant pour importer les amis. Pour
l’instant ils n’ont l’air que de supporter Google Talk au moyen d’un
bot auquel OneSwarm fait des requêtes régulières.
</p><p>S’en suit une description (étonnamment assez peu technique) du
protocole et du fonctionnement interne de OneSwarm.
</p><h2>Avis</h2><p>Le papier en lui même est très accessible et finalement ce n’est pas
tant les connaissances réseaux qui manquent mais plutôt les
connaissances en théorie des graphes. Cependant, une question reste,
bien que partiellement résolue à la fin du papier. Qu’est-ce
qu’apporte au point de vue sécurité et anonymat OneSwarm sur les
réseaux type freenet ? À mon avis OneSwarm repose un peu trop sur le
concept de friend-to-friend pour assurer l’anonymat (en tout cas la
sécurité des transferts en eux-même est très peu évoquée dans le
papier). La réponse apportée par les auteurs est la possibilité
d’avoir des permissions plus fine. À mon avis, OneSwarm représente un
bon compromis entre les performances des réseaux p2p traditionnels et
la sécurité des réseaux p2p anonymes.

</p></div></content></entry>
<entry><title>Génération aléatoire de maps</title><link href="http://blog.iuwt.fr/articles/map-gen"/><id>http://blog.iuwt.fr/articles/map-gen</id><updated>2009-07-23T15:31:29Z</updated><author><name>haveo</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a class="extern" href="http://iuwt.fr/~asmanur/novendiales/">(un)faithful</a><br/></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a class="extern" href="http://iuwt.fr/~asmanur/novendiales/">(un)faithful</a><br/><p>Dans le cadre d’<a class="extern" href="http://iuwt.fr/~asmanur/novendiales/">(un)faithful</a>,
notre projet pour les Novendiales, nous avons eu à coder un générateur
de maps aléatoires. <br/>
</p><p>Traditionnellement, dans un rogue-like les maps ne
sont en effet pas dessinées à l’avance mais générées on-the-fly pour
offrir une plus grande durée de vie (il est courant de recommencer un
rogue-like des dizaines voire des centaines de fois avant de le
finir).
</p><p>De nombreux algorithmes permettent d’accomplir cette tâche. Il y en a
des plus ou moins subtils, plus ou moins efficaces et les résultats
varient grandement d’un algorithme à l’autre. Au final, c’est surtout
une question de goût.
</p><p>Cet article a pour but de décrire l’algorithme utilisé dans
(un)faithful qui sans être révolutionnaire produit des résultats assez
satisfaisants.
</p><p>Imaginez un instant que vous êtes l’heureux possesseur d’un donjon
sous-terrain flambant neuf, malheureusement rempli de terre. La façon
la plus naturelle de construire un dédale de salles et de couloirs
serait de creuser une première salle puis de partir de là en creusant
un peu partout. C’est exactement ce que fait le générateur de maps
d’(un)faithful.
</p><p>Un des aspects important du système est qu’on travaille sur des
briques élémentaires. Dans (un)faithful leur nombre est limité, ce
sont les couloirs et les salles. Les salles se déclinent en plusieurs
briques en fonction des monstres qu’on peut trouver dedans, par
exemple.
</p><p>Pour placer ces briques dans notre niveau en construction on maintient
une liste des "murs", endroits où on peut placer de nouvelles
briques. La liste des briques fournissant des probabilités on choisit
ainsi la brique qu’on veut placer. L’algorithme boucle ensuite jusqu’à
trouver un mur où il peut placer cette brique. Une fois la brique
placée la fonction correspondante renvoie une liste de murs à ajouter
à la liste actuelle et il suffit de continuer. Généralement, on
définit a priori un nombre de briques à placer.
</p><pre><code>  ++++++	    ++++++
  +    +	    +    ++++
  +    O	    +    *  +
  +    +	    +    +  +
  ++++++	    +++++++++
</code></pre><p>O est l’endroit où l’on place la nouvelle pièce.
</p><p>Cette méthode telle quelle présente un gros inconvénient. Les couloirs
ne débouchent génŕalement sur rien ce qui est assez frustrant pour le
joueur et peu réaliste. Ainsi, en tant que post-traitement on parcourt
la map pour supprimer les bouts de couloir inutiles. Deux cas se
présentent alors. Certains couloirs ont simplement besoin d’être
amputés à partir du moment où ils deviennent inutiles. D’autres
doivent être intégralement supprimés, y compris la porte qui y
mène.
</p><pre><code>      ++++++
  OOOOo    *#####OOO
      +    + +++*++
      +    + +    +
      ++++++ +    +
             +    +
             ++++++
</code></pre><p>Les deux types de couloirs à enlever. Les O sont des couloirs qui vont
être supprimés, le o est une porte qui va disparaitre.
</p><p>On parcourt toute la carte précédemment générée et pour chaque case on
teste si c’est : <br/>
</p><ul><li>Un couloir ;
</li><li>Ne touchant aucun autre couloir ;
</li><li>Ne touchant qu’une porte (en effet le cas particulier où deux portes
sont reliées par une seule case est possible).
</li></ul><p>Si la case satisfait ces conditions, on la supprime mais
on supprime également la porte qu’elle touche et le parcours de la map
continue.  Sinon, si la case est :
</p><ul><li>Un couloir ;
</li><li>Touchant un unique autre couloir ;
</li><li>Ne touchant pas de portes ;
</li></ul><p>Auquel cas on supprime le couloir et on recommence les tests sur le
couloir adjacent. Si la case ne vérifie aucune de ces conditions on
continue à parcourir la carte.
</p><p>Cette procédure permet de supprimer tous les couloirs qui ne
débouchent sur rien. Les couloirs qui se divisent sans être utilisés
par la suite sont aussi nettoyés. Dans un premier temps une première
branche est supprimée jusqu’à arriver à l’embranchement qui a 2
voisins donc ne peut pas être suprrimé. Dans un deuxième temps la
seconde branche est supprimée et quand l’algorithme arrive à
l’embranchement il n’a plus qu’un voisin.
</p><p>Au niveau du comportement des briques il est intéressant d’ajouter un
facteur aléatoire, la position de la porte sur la pièce nouvellement
créée afin de rendre les niveaux plus variés. La taille des pièces
peut également varier, en revanche les couloirs sont de taille fixe
puisque de toute façon ils seront nettoyés par la suite.
</p><p>Nous avons aussi utilisé une technique pour tracer des murs plus jolis
qu’un simple caractère. Il existe des caractères Unicode (vous pouvez
aussi travailler avec des symboles ASCII ou des tiles graphiques) qui
représentent tous les embranchements possibles, par exemple : "┼",
"─", "│", "┌", "┬", etc.
</p><pre><code>  ┌─────┐
  │     │
  │     ├──┐
  │     *  │
  └─────┤  │
        └──┘
</code></pre><br/><pre><code>  mur_a_droite + mur_a_gauche * 2 + mur_en_haut * 4 + mur_en_bas * 8
</code></pre><p>qui génère un entier différent pour chacune des 16 configurations
(<code>mur_foo</code> vaut 0 ou 1). Stockez ensuite les 16 tiles correspondantes dans
un tableau (certaines peuvent être identiques) et le tour est joué :-)
. Pour des résultats convenables il faut tout de même associer à
chaque mur un identifiant correspondant au numéro (unique) de la salle
à laquelle il appartient. Un mur n’est alors adjacent à un autre que
s’ils ont le même numéro. Losrqu’une salle partage un mur avec une
autre celui-ci doit alors porter les deux identifiants. On peut aussi
imaginer utiliser cette méthode pour dessiner des portes orientées.
</p><p>Il est enfin intéressant d’apporter un soin particulier à la première
salle générée. Dans notre jeu, celle-ci contient toujours un escalier,
montant ou descendant. Ainsi, il ne nous reste plus qu’à placer
l’autre ce que nous faisons en calculant la case la plus éloignée
(:-’). Cette salle initiale peut aussi être l’antre d’un monstre
particulièrement puissant qui garderait l’escalier. Si vous désirez y
placer un complexe de plusieurs salles formant une suite logique il
est conseillé de ne spécifier qu’une des cases comme mur. Ainsi, le
jeu ne permettra pas au joueur d’accéder à une salle sans passer par
la précédente. Combinée avec notre système d’effets, cette astuce
permet de réaliser des niveaux où le joueur doit rejoindre la première
pièce générée, tuer le boss qu’y s’y trouve et ainsi ouvrir une salle
annexe contenant un escalier descendant.
</p><p>L’article qui m’a inspiré : <a class="extern" href="http://pungentpickles.cm/rlnews/dev00018.html">Dungeon-Building Algorithm - Mike
Anderson</a> .
</p></div></content></entry>
<entry><title>Les types existentiels (2/2)</title><link href="http://blog.iuwt.fr/articles/type-existentiel2"/><id>http://blog.iuwt.fr/articles/type-existentiel2</id><updated>2009-07-14T17:15:00Z</updated><author><name>asmanur</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Maintenant que l'on sait comment implémenter des types existentiels en caml, je vais vous montrer les quelques usages que l'on en fait dans le code pour notre projet pour les novendiales.</p></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Maintenant que l'on sait comment implémenter des types existentiels en caml, je vais vous montrer les quelques usages que l'on en fait dans le code pour notre projet pour les novendiales.</p><p>Maintenant que l'on sait comment implémenter des types existentiels en caml, je vais vous montrer les quelques usages que l'on en fait dans le code pour notre projet pour les novendiales.</p><p>Maintenant que l'on sait comment implémenter des types existentiels en caml, je vais vous montrer les quelques usages que l'on en fait dans le code pour notre projet pour les novendiales.</p><h2 id="State,_l'&#xE9;tat_du_jeu_&#xE0;_tout_instant">State, l'état du jeu à tout instant</h2><p>Dans un jeu il y a principalement trois choses à faire :</p><ul><li><p>dessiner le monde ;</p></li><li><p>réagir lors de l'appui d'une touche ;</p></li><li><p>prendre en compte l'influence du temps.</p></li></ul><p>Supposons que notre type représentant l'état du monde soit <code>'a</code>, cela nous donne le type suivant :</p><div class="highlight"><pre><span class="k">type</span> <span class="k">'</span><span class="n">a</span> <span class="err">é</span><span class="n">tat</span> <span class="o">=</span> <span class="o">{</span>
  <span class="n">dessiner</span> <span class="o">:</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="n">dessin</span> <span class="kt">list</span><span class="o">;</span> 
  <span class="n">temps</span> <span class="o">:</span> <span class="kt">float</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span><span class="o">;</span>
  <span class="n">touche_appuy</span><span class="err">é</span><span class="n">e</span> <span class="o">:</span> <span class="n">touche</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span><span class="o">;</span>
<span class="o">}</span>
</pre></div><p>Quelques commentaires :</p><ul><li><p>seul <code>temps</code> et <code>touche_appuyée</code> sont susceptibles de modifier le</p></li></ul><p> monde,  <code>dessiner</code> s'occupe juste de représenter le monde ;</p><ul><li><p><code>dessiner</code> retourne une liste de <code>dessin</code>, cela permet d'avoir tout</p></li></ul><p> le code de dessin complètement pur et indépendant de la bibliothèque  utilisée  et une fonction <code>dessiner_dessin :  dessin -&gt; unit</code> qui varie selon la bibliothèque utilisée. Les  avantages de cette méthode seront présentés dans un article ultérieur</p><ul><li><p>il n'est pas dit qu'on ait qu'un seul type de monde tout au long de</p></li></ul><p> notre programme, notamment avec les menus ce type peut varier.</p><p>Le problème de ce type c'est qu'il ne permet pas de changer d'état. Il faut donc changer le type de retour de <code>temps</code> et <code>touche_appuyée</code>. Cependant, remplacer <code>'a</code> par quelque chose du genre de</p><div class="highlight"><pre><span class="nc">Continuer</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="o">|</span> <span class="nc">Changement_d_</span><span class="err">é</span><span class="n">tat</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="err">é</span><span class="n">tat</span>
</pre></div><p>ne résoud pas le problème — et si on veut changer de type d'état ? L'intérêt des types existentiels arrive ici, si on a un type <code>état_quantifié</code> on peut écrire simplement</p><div class="highlight"><pre><span class="k">type</span> <span class="k">'</span><span class="n">a</span> <span class="n">retour</span> <span class="o">=</span> <span class="nc">Continuer</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="o">|</span> <span class="nc">Changement_d_</span><span class="err">é</span><span class="n">tat</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="err">é</span><span class="n">tat_quantifi</span><span class="err">é</span>

<span class="k">type</span> <span class="k">'</span><span class="n">a</span> <span class="err">é</span><span class="n">tat</span> <span class="o">=</span> <span class="o">{</span>
  <span class="n">dessiner</span> <span class="o">:</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="n">dessin</span> <span class="kt">list</span><span class="o">;</span> 
  <span class="n">temps</span> <span class="o">:</span> <span class="kt">float</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="n">retour</span><span class="o">;</span>
  <span class="n">touche_appuy</span><span class="err">é</span><span class="n">e</span> <span class="o">:</span> <span class="n">touche</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="n">retour</span><span class="o">;</span>
<span class="o">}</span>
</pre></div><p>Quelques exemples d'état actuellement implémenté (en plus de celui du jeu) :</p><ul><li><p>un état pour les transition graphique entre deux états (l'état est un flottant compris entre 0 et 1) ;</p></li><li><p>un état pour récupérer une touche (désignant un objet) au joueur, l'état est ici le monde (mais ça n'est pas nécéssaire)</p></li><li><p>un état pour sélectionner un ennemi, l'état est ici l'index du monstre sélectionné (qui varie quand l'utilisateur change de monstre).</p></li></ul><h2 id="Des_effets_en_pagaille_!">Des effets en pagaille !</h2><p>Autre utilité des types existentiels : permettre d'avoir une grande variété d'effet.</p><p>En tant que roguelike, notre jeu est en tour par tour et ainsi un effet est grosso modo une fonction qui est appelé à chaque fin de tour pour agir sur le monde d'une façon ou d'une autre. Sauf qu'un effet peut maintenir un état — par exemple le nombre de tour avant qu'il disparaisse, ou plus élaboré si l'effet est moins conventionnel — et donc un type existentiel résoud ce problème.</p><p>Pour appliquer les effets, il suffit ensuite d'itérer sur les effets. En pratique ça pose quelques problèmes de récursivité car le monde dépend des effets et vice-versa or notre effet quantifié est construit via un module … Bref c'est pas la joie.</p></div></content></entry>
<entry><title>Configuration d'une passerelle internet avec iptables</title><link href="http://blog.iuwt.fr/articles/routage"/><id>http://blog.iuwt.fr/articles/routage</id><updated>2009-07-13T19:51:50Z</updated><author><name>haveo</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Vous possédez probablement un appareil dédié au routage chez vous (appareil dédié, routeur-modem ou ce qu'on appelle désormais "box") qui permettent à des machines connectées à un réseau local d'accéder à un autre réseau distant. Comme leur nom l'indique ces appareils ont pour but de rediriger les paquets qu'ils recoivent vers les machines locales qui les ont demandées.</p></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Vous possédez probablement un appareil dédié au routage chez vous (appareil dédié, routeur-modem ou ce qu'on appelle désormais "box") qui permettent à des machines connectées à un réseau local d'accéder à un autre réseau distant. Comme leur nom l'indique ces appareils ont pour but de rediriger les paquets qu'ils recoivent vers les machines locales qui les ont demandées.</p><p>Vous possédez probablement un appareil dédié au routage chez vous (appareil dédié, routeur-modem ou ce qu'on appelle désormais "box") qui permettent à des machines connectées à un réseau local d'accéder à un autre réseau distant. Comme leur nom l'indique ces appareils ont pour but de rediriger les paquets qu'ils recoivent vers les machines locales qui les ont demandées.</p><div class="mdownized"><div class="content"><p>Bien que l'utilisation d'un appareil dédié soit la solution la plus simple, tout le monde ne dispose pas du matériel nécessaire. En réalité, il suffit d'un ordinateur (dédié ou pas) muni de deux interfaces réseau (Wifi et Ethernet ou modem USB et Ethernet, ou deux cartes Ethernet, ...) et d'un réseau local fonctionnel (filaire ou non, peu importe).</p><p>Pour les systèmes utilisant le noyau Linux, la gestion avancée des paquets réseau est la tâche d'iptables. Le configurer afin de rediriger le trafic comme voulu est l'affaire d'une poignée de commandes. En root, avec <code>eth0</code> la connexion au réseau local (composé d'addresses en <code>192.168.0.x</code>) et <code>ath0</code> la connexion à Internet :</p><pre>iptables -A FORWARD -i ath0 -o eth0 -d 192.168.0.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o ath0 -j MASQUERADE
echo 1 &gt; /proc/sys/net/ipv4/ip_forward
</pre><p>Sur les machines clientes il faut essentiellement configurer trois choses : l'IP de votre machine, l'IP de la passerelle et les serveurs DNS. Bien sur la méthode dépend de votre système, sous Linux on ferait par exemple (<code>192.168.0.2</code> pour le client, <code>192.168.0.1</code> pour la passerelle, <code>eth0</code> l'interface réseau du client et également en root) :</p><pre>ifconfig eth0 192.168.0.2
route add default gw 192.168.0.1
</pre><p>Pour les serveurs DNS il faut les ajouter au fichier <code>/etc/resolv.conf</code> sous la forme (il y a un serveur primaire et un secondaire) :</p><pre>nameserver x.x.x.x
nameserver x.x.x.x
</pre><p>Vous pouvez utiliser n'importe quel serveur de votre connaissance le plus simple étant de récupérer le fichier <code>/etc/resolv/conf</code> de la passerelle.</p><p>Après ces manipulations votre réseau est déjà utilisable de manière satisfaisante toutefois si vous désirez avoir une installation permanente quelques ajustements peuvent s'avérer confortables. Les redirections NAT, gérées par iptables permettent à vos ordinateurs locaux de recevoir des connexions externes (ce qui n'est pas évident a priori puisqu'ils partagent une seule addresse IP distante, celle de la passerelle) et un serveur DHCP qui permet aux clients d'obtenir de manière automatique l'IP de la passerelle, une IP pour leur machine et des serveurs DNS (la commande sous Linux est <code>dhclient interface</code>).</p><p>En espérant que ce mini-howto aurait été utile :-) ,</p></div></div></div></content></entry>
<entry><title>Les types existentiels (1/2)</title><link href="http://blog.iuwt.fr/articles/type-existentiel1"/><id>http://blog.iuwt.fr/articles/type-existentiel1</id><updated>2009-07-13T19:22:31Z</updated><author><name>asmanur</name></author><category term="Articles"/><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>
Pour les novendiales, notre code utilise ce qu'on appelle les 
  <i>types existentiels</i>
. C'est un aspect assez peu connu et assez délicat des langages fonctionnels et donc assez peu répandu. Pourtant, cette technique a des ressemblances avec ce qui peut se faire du coté de l'orienté de l'objet (en fait cela permet une encapsulation).
</p></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>
Pour les novendiales, notre code utilise ce qu'on appelle les 
  <i>types existentiels</i>
. C'est un aspect assez peu connu et assez délicat des langages fonctionnels et donc assez peu répandu. Pourtant, cette technique a des ressemblances avec ce qui peut se faire du coté de l'orienté de l'objet (en fait cela permet une encapsulation).
</p><p>
Pour les novendiales, notre code utilise ce qu'on appelle les 
  <i>types existentiels</i>
. C'est un aspect assez peu connu et assez délicat des langages fonctionnels et donc assez peu répandu. Pourtant, cette technique a des ressemblances avec ce qui peut se faire du coté de l'orienté de l'objet (en fait cela permet une encapsulation).
</p><h2 id="G&#xE9;n&#xE9;ralit&#xE9;s">Généralités</h2><p>Le quantifieur universel est le plus utilisé et le plus facilement compréhensible, il est d'ailleurs implicite dans la plupart des langages fonctionnels. Par exemple, le type de <code>map</code>,</p><div class="highlight"><pre><span class="o">(</span><span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">b</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="k">'</span><span class="n">a</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">b</span> <span class="kt">list</span><span class="o">)</span>
</pre></div><p>si il devait être quantifié, donnerait</p><pre>forall 'a 'b, ('a -&gt; 'b) -&gt; ('a list -&gt; 'b list)
</pre><p>Le quantifieur universel permet donc une généricité à travers le <i>polymorphisme paramétrique</i>.</p><p>Formellement, si on écrit <code>type t = forall 'a. 'a -&gt; 'a</code>, le type des fonctions acceptant un paramètre de n'importe quel type et renvoyant une valeur de ce même type (<code>t</code> est habité uniquement par la fonction identité), on voit bien que <code>t</code> ne dépend plus de <code>'a</code>.</p><p>Le but serait donc de pouvoir écrire par analogie <code>type t = exists 'a. 'a -&gt; 'a</code>. Que représente ce type ? Il représente les fonctions telles qu'il existe un type <code>'a</code> tel que la fonction soit de type <code>'a -&gt; 'a</code>, la fonction n'est donc pas générique. Par exemple, <code>(fun n -&gt; n + 1)</code> est un membre de type <code>t</code>.</p><p>L'avantage d'un tel type c'est qu'il ne dépend pas de <code>'a</code> donc on peut en construire une liste.</p><p>On peut imaginer avoir un <code>type t = exists 'a. ('a * ('a -&gt; 'a))</code>. Ainsi ce type représente les couples de la forme <code>(état, transition)</code> avec <code>état</code> n'importe quel type. Par exemple on pourrait avoir une liste de ce type </p><div class="highlight"><pre><span class="o">[(</span><span class="mi">4</span><span class="o">,</span> <span class="n">succ</span><span class="o">);</span> <span class="o">(</span><span class="s2">"foo"</span><span class="o">,</span> <span class="o">(</span> <span class="o">(^)</span> <span class="s2">"f"</span> <span class="o">)]</span> <span class="o">:</span> <span class="n">t</span> <span class="kt">list</span>
</pre></div><p>Ensuite, on pourrait définir une fonction <code>update : t list -&gt; t list</code> qui à chaque couple <code>(état, transition)</code> associe <code>(transition état, transition)</code>.</p><p>Cela permet ainsi de mettre à jour plusieurs chose complètement différente d'un coup — des personnages, des objets, etc. — et tout mettre à jour tout d'un coup sans se préoccuper de rien.</p><h2 id="Impl&#xE9;mentation">Implémentation</h2><p>OCaml ne dispose pas de base de ces types existentiels mais il est possible de de les implémenter (avec une légère surcharge syntaxique).</p><p>En fait, en logique, on peut exprimer le quantifieur existentiel à l'aide de la négation et du quantifieur universel. On a l'identité ∃x. P(x) ≣ ¬ (∀x. ¬ P(x)) (¬ étant la négation) (il s'agit simplement d'une trivialité facile à se représenter). Si on sait trouver une représentation sympathique de la négation, en exploitant l'<a href="http://da-bhm.org/see.yaws?id=21">analogie de Curry-Howard</a>, on pourra trouver une représentation du type <code>exists 'a. P a</code>. La définition usuelle de ¬P est P ⇒ ⊥ (⊥ se lisant « faux »). Sachant que la proposition fausse implique toute proposition <sup><a id="footnotes1"/><a href="#footnotes_1">1</a></sup>, on a ¬P ≣ ∀Q. P ⇒ Q.</p><p>On finit par aboutir à un encodage du type ∀R. (∀x. P(x) ⇒ R) ⇒ R (pas strictement équivalent à l'existence ceci dit).</p><p>Intéressons nous tout d'abord, à (∀x. P(x) ⇒ R) pour un certain R. Le type équivalent serait <code>forall 'a. 'a P -&gt; R</code>. Il s'agit ici du type des <i>opérateurs</i> agissant sur n'importe <code>'a P</code> et renvoyant un <code>R</code> <i>indépendant</i> de <code>'a</code>. Pour définir un tel type polymorphe en caml, une des façons de procéder est d'utiliser des <i>records</i>. Ainsi le type des opérateurs est </p><div class="highlight"><pre><span class="k">type</span> <span class="k">'</span><span class="nc">R</span> <span class="n">op</span><span class="err">é</span><span class="n">rateur</span> <span class="o">=</span> <span class="o">{</span> <span class="n">op</span> <span class="o">:</span> <span class="k">'</span><span class="n">x</span><span class="o">.</span> <span class="k">'</span><span class="n">x</span> <span class="nc">P</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="nc">R</span> <span class="o">}</span>
</pre></div><p>Le <code>'x.</code> signifie <code>forall x.</code>.</p><p>Maintenant, on peut trouver un équivalent du type complet :</p><pre>(forall R. R operateur) -&gt; R
</pre><p>Soit en caml :</p><div class="highlight"><pre><span class="k">type</span> <span class="n">t</span> <span class="o">=</span> <span class="o">{</span> <span class="n">app</span> <span class="o">:</span> <span class="k">'</span><span class="nn">R</span><span class="p">.</span> <span class="err">'</span><span class="nc">R</span> <span class="n">op</span><span class="err">é</span><span class="n">rateur</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="nc">R</span> <span class="o">}</span>
</pre></div><p>Un objet de ce type est un élément qui prend un opérateur en argument et l'appelle sur une donnée qui est ici cachée. On peut ainsi écrire une fonction <code>pack</code> qui convertit un <code>'x P</code> en un type <code>t</code> (cachant la valeur dans une closure) :</p><div class="highlight"><pre><span class="k">let</span> <span class="n">pack</span> <span class="err">é</span><span class="n">l</span><span class="err">é</span><span class="n">ment</span> <span class="o">=</span> <span class="o">{</span> <span class="n">app</span> <span class="o">=</span> <span class="o">(</span><span class="k">fun</span> <span class="o">{</span><span class="n">op</span> <span class="o">=</span> <span class="n">op</span><span class="err">é</span><span class="n">rateur</span><span class="o">}</span> <span class="o">-&gt;</span> <span class="n">op</span><span class="err">é</span><span class="n">rateur</span> <span class="err">é</span><span class="n">l</span><span class="err">é</span><span class="n">ment</span><span class="o">)</span> <span class="o">}</span>
</pre></div><p>Il y a là le principe des types existentiels ; cacher un type dans une closure pour offrir une interface commune. C'est une possibilité  également offerte par les langages orientés objets (et elle est nettement plus utilisée dans les langages OO ceci dit).</p><p>Rappelons que notre type que l'on veut quantifier est <code>'a * ('a -&gt; 'a)</code>. Imaginons que nous souhaitons écrire une fonction <code>next</code> qui retourne l'état suivant. Il s'agit ici d'un opérateur que l'on peut définir ainsi :</p><div class="highlight"><pre><span class="k">let</span> <span class="n">suivant</span> <span class="o">=</span> <span class="o">{</span> <span class="n">op</span> <span class="o">=</span> <span class="o">(</span><span class="k">fun</span> <span class="o">(</span><span class="err">é</span><span class="n">tat</span><span class="o">,</span> <span class="n">transition</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">pack</span> <span class="o">(</span><span class="n">transition</span> <span class="err">é</span><span class="n">tat</span><span class="o">,</span> <span class="n">transition</span><span class="o">)</span> <span class="o">}</span>
</pre></div><p>Il ne faut pas oublier d'empaqueter le résultat au risque de rencontrer une vive réaction de caml. Pour l'appliquer il suffit de faire :</p><div class="highlight"><pre><span class="o">(</span><span class="n">pack</span> <span class="o">(</span><span class="mi">4</span><span class="o">,</span> <span class="n">succ</span><span class="o">)).</span><span class="n">app</span> <span class="n">suivant</span>
</pre></div><p>À propos de l'exemple : si on est sûr que l'interface ne bougera pas, on peut extrêment simplifier le problème en identifiant un objet à son interface — en faisant un record de fonctions par exemple — ceci dit si l'interface est amenée à évoluer ce n'est pas forcément pratique.</p><p>Voici le code au complet, avec une petite variation au niveau des opérateurs, afin de leur permettre de prendre un paramètre en plus, c'est bien utile.</p><div class="highlight"><pre><span class="k">module</span> <span class="nc">Make</span> <span class="o">(</span><span class="nc">T</span> <span class="o">:</span> <span class="k">sig</span> <span class="k">type</span> <span class="k">'</span><span class="n">a</span> <span class="n">t</span> <span class="k">end</span><span class="o">)</span> <span class="o">:</span> <span class="k">sig</span>
  <span class="k">type</span> <span class="o">(</span><span class="k">'</span><span class="n">b</span><span class="o">,</span> <span class="k">'</span><span class="n">c</span><span class="o">)</span> <span class="n">op</span> <span class="o">=</span> <span class="o">{</span> <span class="n">op</span><span class="o">:</span> <span class="k">'</span><span class="n">a</span><span class="o">.</span> <span class="k">'</span><span class="n">b</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="nn">T</span><span class="p">.</span><span class="n">t</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span> <span class="o">}</span>
  <span class="k">type</span> <span class="n">t</span>
  <span class="k">val</span> <span class="n">pack</span> <span class="o">:</span> <span class="k">'</span><span class="n">a</span> <span class="nn">T</span><span class="p">.</span><span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span>
  <span class="k">val</span> <span class="n">exec</span> <span class="o">:</span> <span class="o">(</span><span class="k">'</span><span class="n">b</span><span class="o">,</span> <span class="k">'</span><span class="n">c</span><span class="o">)</span> <span class="n">op</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="k">'</span><span class="n">b</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span><span class="o">)</span>
<span class="k">end</span> 
<span class="o">=</span> <span class="k">struct</span>
  <span class="k">type</span> <span class="o">(</span><span class="k">'</span><span class="n">b</span><span class="o">,</span> <span class="k">'</span><span class="n">c</span><span class="o">)</span> <span class="n">op</span> <span class="o">=</span> <span class="o">{</span> <span class="n">op</span><span class="o">:</span> <span class="k">'</span><span class="n">a</span><span class="o">.</span> <span class="k">'</span><span class="n">b</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">a</span> <span class="nn">T</span><span class="p">.</span><span class="n">t</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span> <span class="o">}</span>

  <span class="k">type</span> <span class="n">t</span> <span class="o">=</span> <span class="o">{</span> <span class="n">obj</span><span class="o">:</span> <span class="k">'</span><span class="n">b</span> <span class="k">'</span><span class="n">c</span><span class="o">.</span> <span class="k">'</span><span class="n">b</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="k">'</span><span class="n">b</span><span class="o">,</span> <span class="k">'</span><span class="n">c</span><span class="o">)</span> <span class="n">op</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span> <span class="o">}</span>
  
  <span class="k">let</span> <span class="n">pack</span> <span class="n">obj</span> <span class="o">=</span> <span class="o">{</span>
    <span class="n">obj</span> <span class="o">=</span> <span class="o">(</span><span class="k">fun</span> <span class="n">x</span> <span class="o">{</span><span class="n">op</span><span class="o">=</span><span class="n">op</span><span class="o">}</span> <span class="o">-&gt;</span> <span class="n">op</span> <span class="n">x</span> <span class="n">obj</span><span class="o">)</span>
  <span class="o">}</span>

  <span class="k">let</span> <span class="n">exec</span> <span class="n">f</span> <span class="n">x</span> <span class="o">{</span><span class="n">obj</span> <span class="o">=</span> <span class="n">o</span><span class="o">}</span> <span class="o">=</span> <span class="n">o</span> <span class="n">x</span> <span class="n">f</span>
<span class="k">end</span>
</pre></div><p>Au programme de demain : des exemples d'applications dans notre projet. Stay tuned !</p><hr/><p>Notes :</p><p>[<a id="footnotes_1"/><a href="#footnotes1">1</a>] Il n'est pas évident que ce soit à la seule à vérifier cette propriété, mais étant donné les propriétés de ⇒, si A vérifie également cette propriété, on a immédiatement, ⊥ ⇔ A</p></div></content></entry></feed>
