<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OCaml de pied en cap</title>
	<atom:link href="https://blog.developpez.com/ocamlblog/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/ocamlblog</link>
	<description></description>
	<lastBuildDate>Fri, 22 Mar 2013 03:12:26 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Héberger un code source</title>
		<link>https://blog.developpez.com/ocamlblog/p10169/informatique/title_200</link>
		<comments>https://blog.developpez.com/ocamlblog/p10169/informatique/title_200#comments</comments>
		<pubDate>Sun, 31 Jul 2011 19:45:17 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[Logiciels]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour, Toute personne qui programme finit un jour ou l&#8217;autre par se demander où héberger son code source en vue de lui donner une bonne visibilité. Les solutions traditionnelles, comme SourceForge, sont aujourd&#8217;hui concurrencées par de nombreuses autres plateformes. Je vous propose de vous présenter aujourd&#8217;hui GitHub. GitHub est un service web d&#8217;hébergement et de gestion de développement de logiciels qui repose sur l&#8217;utilisation du programme Git. C&#8217;est un système rapide, léger et simple d&#8217;utilisation [&#8230;]]]></description>
				<content:encoded><![CDATA[<table>
<tr>
<td><a href="https://github.com/" target="_blank"><img src="https://a248.e.akamai.net/assets.github.com/images/modules/header/logov6-hover.png" height="80" width="140" /></a></td>
</tr>
</table>
<p>Bonjour,</p>
<p>Toute personne qui programme finit un jour ou l&rsquo;autre par se demander où héberger son code source en vue de lui donner une bonne visibilité. Les solutions traditionnelles, comme <a href="http://sourceforge.net/">SourceForge</a>, sont aujourd&rsquo;hui concurrencées par de nombreuses autres plateformes. Je vous propose de vous présenter aujourd&rsquo;hui <a href="https://github.com/">GitHub</a>.</p>
<p><span id="more-10"></span></p>
<p><a href="https://github.com/">GitHub</a> est un service web d&rsquo;hébergement et de gestion de développement de logiciels qui repose sur l&rsquo;utilisation du programme <a href="http://git-scm.com/">Git</a>. C&rsquo;est un système rapide, léger et simple d&rsquo;utilisation qui présente au moins deux avantages sur ses concurrents :</p>
<ul>
<li>Il est très facile de faire un <em>fork</em> d&rsquo;un projet.</li>
<li>Les fichiers peuvent être édités directement sur le site.</li>
</ul>
<p>Comme la plupart de ses concurrents, GitHub permet aussi d&rsquo;ajouter un <a href="http://fr.wikipedia.org/wiki/Wiki">wiki</a>, de la documentation, une mailing list, un <em>bug tracker</em>, et bien d&rsquo;autres choses.</p>
<p>Pour ceux qui tiennent à jour un blog (plus ou moins régulièrement d&rsquo;ailleurs), la possibilité d&rsquo;avoir accès à une <a href="http://en.wikipedia.org/wiki/Pastebin">pastebin</a> qui se comporte comme un dépôt Git indépendant est une chose vraiment appréciable.</p>
<p><img src="http://blog.developpez.com/media/GitHub.jpg" width="800" height="480" alt="Page principale de GitHub" /></p>
<p>J&rsquo;aurai l&rsquo;occasion de vous en reparler prochainement à l&rsquo;occasion de la présentation, sur ce blog, d&rsquo;une petite bibliothèque dédiée aux listes doublement chaînées circulaires immutables (pour ceux qui se souviennent, j&rsquo;ai déjà eu l&rsquo;occasion de présenter <a href="http://blog.developpez.com/ocamlblog/p8214/programmation-fonctionnelle/ocaml/listes-doublement-chaines-circulaires/">la version mutable</a> sur ce blog).</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Créer un panorama avec Hugin</title>
		<link>https://blog.developpez.com/ocamlblog/p10135/photo/creer_un_panorama_avec_hugin</link>
		<comments>https://blog.developpez.com/ocamlblog/p10135/photo/creer_un_panorama_avec_hugin#comments</comments>
		<pubDate>Sun, 17 Jul 2011 19:36:42 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Photo]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Le logiciel libre Hugin est une interface graphique pour la suite logicielle Panorama Tools. Il permet de réaliser des panoramas de qualité à partir d&#8217;une série d&#8217;images chevauchantes. Les algorithmes mis en œuvre permettent de corriger les variations d&#8217;exposition, fréquentes lorsqu&#8217;on utilise un réflex numérique en mode automatique, ainsi que de multiples paramètres liés à l&#8217;objectif et/ou à la manière dont le photographe a réalisé ses clichés. Ce tutoriel offre un aperçu des possibilités du [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><center></p>
<table>
<tr>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://hugin.sourceforge.net/css/hugin-icon.png"/></a></td>
</tr>
</table>
<p></center></p>
<p>Le logiciel libre <strong>Hugin</strong> est une interface graphique pour la suite logicielle <strong>Panorama Tools</strong>. Il permet de réaliser des panoramas de qualité à partir d&rsquo;une série d&rsquo;images chevauchantes. Les algorithmes mis en œuvre permettent de corriger les variations d&rsquo;exposition, fréquentes lorsqu&rsquo;on utilise un réflex numérique en mode automatique, ainsi que de multiples paramètres liés à l&rsquo;objectif et/ou à la manière dont le photographe a réalisé ses clichés. Ce tutoriel offre un aperçu des possibilités du logiciel ne vise donc pas à l&rsquo;exhaustivité. Les lecteurs qui souhaitent approfondir leur connaissance de cet outil pourront consulter avec profit la documentation officielle.</p>
<p><span id="more-23"></span></p>
<h2>Point de départ : plusieurs images d&rsquo;un même paysage</h2>
<p>Lors d&rsquo;une randonnée, il n&rsquo;est pas rare de trouver des paysages à couper le souffle, notamment des cirques et des chaînes de montagnes qu&rsquo;il n&rsquo;est pas possible de capturer en une seule fois, même avec un très grand angle. Dans ce cas, on peut prendre plusieurs clichés et les assembler pour former un diaporama. Dans ce tutoriel, nous allons partir des trois images suivantes :</p>
<p><center></p>
<table>
<tr>
<td><strong>Gauche</strong></td>
<td><strong>Centre</strong></td>
<td><strong>Droite</strong></td>
</tr>
<tr>
<td><img src="http://blog.developpez.com/media/hugin_lpicture.jpg" width="220" height="150" alt="Image de gauche pour le panorama" /></td>
<td><img src="http://blog.developpez.com/media/hugin_mpicture.jpg" width="220" height="150" alt="Image du milieu pour le panorama" /></td>
<td><img src="http://blog.developpez.com/media/hugin_rpicture.jpg" width="220" height="150" alt="Image de droite pour le panorama" /></td>
</tr>
</table>
<p></center></p>
<p>Nous allons charger ces trois images dans Hugin et lui demander de les assembler pour former un panorama. Nous pourrions aussi le faire manuellement ou à l&rsquo;aide de logiciels spécifiques (tel que Canon Photo Stitch). Cependant, nous allons voir que le rendu obtenu avec Hugin est presque toujours optimal, ce qui n&rsquo;est pas le cas avec d&rsquo;autres logiciels. On commence donc par lancer le logiciel, qui se présente sous cette forme :</p>
<p><center><img src="http://blog.developpez.com/media/hugin_main_window.png" width="800" height="533" alt="" /></center></p>
<p>Cette fenêtre comporte un onglet <strong>Assistant</strong>, ouvert dès le démarrage de l&rsquo;application, qui propose une procédure simplifiée de création de panoramas, en trois étapes : (1) chargement des images, (2) alignement et (3) assemblage des panneaux pour former le diaporama. L&rsquo;autre méthode permet de régler plus de paramètres mais nécessite de plus amples explications. Elle fait appel à tous les autres onglets de la fenêtre : <strong>Images</strong>, <strong>Appareil photo et objectifs</strong>, <strong>Recadrer</strong>, <strong>Points de contrôle</strong>, <strong>Optimisations</strong>, <strong>Exposition</strong> et <strong>Assemblage</strong>. C&rsquo;est cette seconde méthode que nous allons présenter ici.</p>
<h2>Sélection des images à assembler</h2>
<p>Commençons donc par nous rendre dans l&rsquo;onglet <strong>Images</strong>. On ajoute ensuite la série d&rsquo;images qui compose le panorama (&laquo;&nbsp;Ajouter des images individuelles&nbsp;&raquo;), ce qui nous donne quelque chose comme ceci :</p>
<p><center><img src="http://blog.developpez.com/media/hugin_add_points.jpg" width="800" height="533" alt="" /></center></p>
<h2>Création de points de contrôle dans les zones de recouvrement</h2>
<p>On se dirige ensuite vers le bouton &laquo;&nbsp;Créer des points de contrôle&nbsp;&raquo;. Ceci indique à Hugin que nous voulons qu&rsquo;il chercher des <strong>zones de recouvrement</strong> entre les différentes images. Dans ces zones, des points sont utilisés comme repères pour l&rsquo;alignement. Nous verrons ensuite qu&rsquo;il est possible d&rsquo;optimiser les points choisis (mais Hugin s&rsquo;en sort généralement très bien, même avec la configuration de base). Pour information, j&rsquo;ai coutume de demander 100 points de contrôle, mais on peut demander moins et c&rsquo;est généralement suffisant.</p>
<p>On peut contrôler les points qui ont été trouvés dans l&rsquo;onglet <strong>Points de contrôle</strong> :</p>
<p><center><img src="http://blog.developpez.com/media/Hugin_details_points.jpg" width="800" height="533" alt="" /></center></p>
<p>Si le nombre de points de contrôle est insuffisant, il est possible d&rsquo;en ajouter de nouveaux et/ou d&rsquo;en définir manuellement. On peut également utiliser l&rsquo;outil <strong>Céleste</strong> pour supprimer les points de contrôle situés dans le ciel (parfois moins fiables).</p>
<h2>Optimisations diverses</h2>
<p>On peut ensuite effectuer des optimisations sur les <strong>distorsions</strong> (distorsion en barillet, mouvements de tangage ou de roulis lors des prises de vues, etc.) et l&rsquo;<strong>exposition des images</strong> (gamme dynamique faible ou étendue, balance des blancs fixe ou variable d&rsquo;un cliché à l&rsquo;autre, etc.) :</p>
<p><center><img src="http://blog.developpez.com/media/hugin_optimize.png" width="800" height="533" alt="" /></center><br />
<center><img src="http://blog.developpez.com/media/hugin_exposition.png" width="800" height="533" alt="" /></center></p>
<h2>Assemblage final et création du panorama</h2>
<p>Il ne reste plus alors qu&rsquo;à réaliser l&rsquo;<strong>assemblage</strong> proprement dit, et obtenir ainsi un panorama. Là encore, de nombreux réglages sont possibles, notamment le type de <strong>projection</strong> utilisé (cylindrique, rectilinéaire, Mercator, etc.), le <strong>recadrage</strong> automatique ou encore la <strong>manière dont Hugin doit construire le panorama final</strong> (fusion de l&rsquo;exposition, fusion de type HDR, etc.)</p>
<p><center><img src="http://blog.developpez.com/media/Hugin_assemblage.jpg" width="800" height="533" alt="" /></center></p>
<p>L&rsquo;image ci-dessus vous montre qu&rsquo;il est possible de contrôler le <strong>type d&rsquo;image en sortie</strong> (image JPEG, TIFF, etc.) et la <strong>nature des outils utilisés pour l&rsquo;assemblage</strong>.</p>
<p>Et voici la récompense :</p>
<p><center><img src="http://blog.developpez.com/media/Hugin_final.jpg" width="800" height="397" alt="" /></center></p>
<p>Vous trouverez ici l&rsquo;<a href="http://www.flickr.com/photos/cacophrene/5674866469/">image en taille originale</a>.</p>
<p>Pour finir, voici quelques autres exemples de panoramas créés avec Hugin :</p>
<ul>
<li><a href="http://www.flickr.com/photos/cacophrene/5674813113/">Un autre panorama de neige pris au même endroit</a></li>
<li><a href="http://www.flickr.com/photos/cacophrene/5050319550/">Un panorama plus ambitieux constitué de 10 images</a></li>
</ul>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Polymorphic variants and type constraints</title>
		<link>https://blog.developpez.com/ocamlblog/p9536/informatique/polymorphic_variants_and_type_constraint</link>
		<comments>https://blog.developpez.com/ocamlblog/p9536/informatique/polymorphic_variants_and_type_constraint#comments</comments>
		<pubDate>Sun, 28 Nov 2010 19:09:53 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Hello everyone, Yesterday, a pretty young woman told me that writing about programming languages really need to be done in English. Now that I know she reads my humble texts, I have to follow its advice. So now&#8230; let’s be a bit more serious. As you probably know, polymorphic variants are often seen as a light and elegant alternative to OCaml constructors. Yet these two concepts are not completely interchangeable : that is why they [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Hello everyone,</p>
<p><center></p>
<table>
<tr>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>	Yesterday, a pretty young woman told me that writing about programming languages really need to be done in English. Now that I know she reads my humble texts, I have to follow its advice. So now&#8230; let’s be a bit more serious. <strong><img src="https://blog.developpez.com/ocamlblog/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /></strong><br />
	As you probably know, polymorphic variants are often seen as a light and elegant alternative to OCaml constructors. Yet these two concepts are not completely interchangeable : that is why they coexist in OCaml. Here I describe a possible use of polymorphic variants to refine some function signatures from the OCaml standard library by adding type constraints.</p>
<p><span id="more-9"></span></p>
<h3>Too simple to be safe ?</h3>
<p>	To make things clearer, let’s consider the example of floating-point numbers (or simply floats) and their corresponding OCaml type. The core module defines many functions which deal with floats, such as <strong>square root</strong> (<code class="codecolorer text default"><span class="text">sqrt</span></code>) and <strong>exponential</strong> (<code class="codecolorer text default"><span class="text">exp</span></code>). Both have the same signature :</p>
<pre><strong>val</strong> sqrt : float -> float
<strong>val</strong> exp : float -> float</pre>
<p>	This short signature means that the function receives a floating-point number as argument and returns a floating-point number as result. Now try to remember your math lessons. You should probably know that exponential is defined from <strong>R</strong> to <strong>R+</strong>, whereas square root is defined from <strong>R+</strong> to <strong>R+</strong>. In other words, there is no way to compute the square root of strictly negative numbers. As a consequence, we expect the <code class="codecolorer text default"><span class="text">sqrt</span></code> function to raise an exception when the input value is negative. Actually, instead of raising an exception, the IEEE 754 standard introduces a special float value called <code class="codecolorer text default"><span class="text">NaN</span></code> (<em>not a number</em>) to handle these cases.</p>
<pre># let x = -3.5;;
val x : float = -3.5
# exp x;;
- : float = 0.0301973834223185
# sqrt x;;
- : float = nan</pre>
<p>	Due to its special status, <code class="codecolorer text default"><span class="text">NaN</span></code> is <strong>only able to spread again and again through successive computations</strong>, so that the final value is always <code class="codecolorer text default"><span class="text">NaN</span></code>. This is a convenient solution&#8230; but we can also try to get more explicit signatures. This can be achieved with <strong>polymorphic variants</strong> <em>(this is false suspense before true headache)</em>.</p>
<h3>The strange world of polymorphic variants</h3>
<p>	Polymorphic variants are quite subtle stuff that I cannot explain in one short sentence. Therefore, people who are not familiar with this concept should refer to the OCaml manual <em>(politeness for RTFM)</em>. For the others, the only thing I really expect you to know by heart is that <code class="codecolorer text default"><span class="text">[&gt; `Foo]</span></code> means « at least `Foo, but maybe something else », whereas <code class="codecolorer text default"><span class="text">[&lt; `Foo | `Bar]</span></code> means « nothing more than <code class="codecolorer text default"><span class="text">`Foo</span></code> and <code class="codecolorer text default"><span class="text">`Bar</span></code> ». This is the key of what we are going to do in this short article. So first, let’s define a new type :</p>
<pre>type ‘a t = ‘a * float</pre>
<p>	The polymorphic parameter will be used to add some <strong>flags</strong> to the float value. These flags are polymorphic variants. Of course, the whole definition will be left <strong>abstract</strong> in the module signature. Then we need two functions for building positive and negative numbers floats, and a specialized value for zero :</p>
<pre><strong>let</strong> zero = (‘Nil, 0.0)
<strong>let</strong> positive flo = <strong>if</strong> flo > 0. <strong>then</strong> (‘Pos, flo) <strong>else</strong> invalid_arg "positive"
<strong>let</strong> negative flo = <strong>if</strong> flo < 0. <strong>then</strong> (‘Neg, flo) <strong>else</strong> invalid_arg "negative"</pre>
<p>These functions have quite clear signatures :</p>
<pre><strong>val</strong> zero : [> ‘Nil] t
<strong>val</strong> positive : float -> [> ‘Pos] t
<strong>val</strong> negative : float -> [> ‘Neg] t</pre>
<p>	Note the use of <code class="codecolorer text default"><span class="text">&gt;</span></code> in the above signatures and try to understand its aim. Then let’s write another function to convert a given <code class="codecolorer text default"><span class="text">t</span></code> value to floating-point number :</p>
<pre><strong>let</strong> to_float = snd</pre>
<p>And that’s all ! We are now able to make things a bit safer. One example :</p>
<pre><strong>let</strong> sqrt = <strong>function</strong> ‘Nil, _ -> zero | ‘Pos, x -> (‘Pos, sqrt x)</pre>
<p>This new <code class="codecolorer text default"><span class="text">sqrt</span></code> function has a fairly explicit <em>(maybe also cryptic)</em> signature :</p>
<pre><strong>val</strong> sqrt : [< ‘Nil | ‘Pos] t -> [> ‘Nil | ‘Pos] t</pre>
<p>	It just means that sqrt expects a non-negative float (either <code class="codecolorer text default"><span class="text">`Nil</span></code> or <code class="codecolorer text default"><span class="text">`Pos</span></code>) and returns a non-negative float. Wonderful ! Now we can write many more float-related functions :</p>
<pre><strong>let</strong> exp t = (‘Pos, exp (to_float t))
<strong>let</strong> log (‘Pos, x) =
  <strong>let</strong> r = log x <strong>in</strong>
  <strong>if</strong> r = 0. <strong>then</strong> zero
  <strong>else</strong> <strong>if</strong> r < 0. <strong>then</strong> (‘Neg, x) <strong>else</strong> (‘Pos, x)
<strong>let</strong> abs t =
  <strong>let</strong> flo = abs_float (to_float t) <strong>in</strong>
  <strong>if</strong> flo = 0. <strong>then</strong> zero <strong>else</strong> (‘Pos, flo)</pre>
<p>and their signatures :</p>
<pre><strong>val</strong> exp : [< ‘Neg| ‘Nil | ‘Pos] t -> [> ‘Pos] t
<strong>val</strong> log : [‘Pos] t -> [> ‘Neg| ‘Nil | ‘Pos] t
<strong>val</strong> abs : ‘a t -> [> ‘Nil | ‘Pos] t</pre>
<p>	We cannot do unsafe computations by composing functions anymore. For instance, the following code is rejected at compile time :</p>
<pre><strong>let</strong> sqrt_log x = sqrt (log x)
                      ^^^^^^^
Error: This expression has type
         ([> `Neg | `Nil | `Pos ] as 'a) t = 'a * float
       but an expression was expected of type
         ([< `Nil | `Pos ] as 'b) t = 'b * float
       The second variant type does not allow tag(s) `Neg</pre>
<p>	The compiler is complaining because <code class="codecolorer text default"><span class="text">log</span></code> can return negative values, and <code class="codecolorer text default"><span class="text">sqrt</span></code> cannot handle them. We should thus write something like this :</p>
<pre><strong>type</strong> nonneg = [`Nil | `Pos]

<strong>let</strong> sqrt_log x =
  <strong>match</strong> log x <strong>with</strong>
  | `Neg, _ -> None
  | #nonneg, _ <strong>as</strong> y -> Some (sqrt y)</pre>
<p>and we get the following signature :</p>
<pre><strong>val</strong> sqrt_log : [ `Pos ] t -> [&gt; `Nil | `Pos ] t option</pre>
<p>That’s all folks !<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Journées Méditerranéennes du Logiciel Libre (JM2L 2010)</title>
		<link>https://blog.developpez.com/ocamlblog/p9537/actualites/journees_mediterraneennes_du_logiciel_li_2010</link>
		<comments>https://blog.developpez.com/ocamlblog/p9537/actualites/journees_mediterraneennes_du_logiciel_li_2010#comments</comments>
		<pubDate>Sun, 28 Nov 2010 19:36:10 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Actualités]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour, J’ai eu l’occasion d’assister ce week-end à trois conférences données dans le cadre des Journées Méditerranéennes du Logiciel Libre (JM2L). Comme il est connu qu’on ne râle jamais mieux qu’en français, cette partie ne sera pas écrite dans la langue de Shakespeare. Création d’un logiciel libre, Thibault Duponchelle Robots mobiles et autonomes contrôlés avec Pharo, Noury Bouraqadi Autour de SSL/TSL, Philippe Lhardy Je tiens d’emblée à dire que l’avis formulé ici n’engage que moi. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour,</p>
<p><center></p>
<table>
<tr>
<td><a href="http://jm2l.linux-azur.org/" target="_blank"><img src="http://jm2l.linux-azur.org/sites/jm2l.linux-azur.org/files/image/2010/affiche2010.png" height="250"/></a></td>
</tr>
</table>
<p></center></p>
<p>J’ai eu l’occasion d’assister ce week-end à trois conférences données dans le cadre des Journées Méditerranéennes du Logiciel Libre (JM2L). Comme il est connu qu’on ne râle jamais mieux qu’en français, cette partie ne sera pas écrite dans la langue de Shakespeare.</p>
<ul>
<li><em>Création d’un logiciel libre</em>, Thibault Duponchelle</li>
<li><em>Robots mobiles et autonomes contrôlés avec Pharo</em>, Noury Bouraqadi</li>
<li><em>Autour de SSL/TSL</em>, Philippe Lhardy</li>
</ul>
<p>	Je tiens d’emblée à dire que l’avis formulé ici n’engage que moi. Le ton adopté est volontairement critique, sans doute un peu exagérateur, mais c’est à dessein : on apprend en tombant.</p>
<p><span id="more-21"></span></p>
<h3>Création d’un logiciel libre</h3>
<p>	Cette première conférence avait pour objectif de présenter dans leurs grandes lignes les recettes qui permettent de concevoir efficacement un logiciel libre. Le conférencier a donc rappelé les conseils donnés aux programmeurs (documentez-vous, posez des questions, entraînez-vous, etc.) et mis l’accent sur le grand nombre d’outils accessibles pour faciliter le travail (débogueurs, générateurs de documentation, logiciels de gestion de versions, forges, etc.). Je regrette que la seule application évoquée lors de la conférence (un émulateur de calculatrices) ait un point de départ juridiquement contestable et ne présente qu’un intérêt très limité. Franchement, si j’accepte de payer 200€ une calculatrice aux airs de petit ordinateur, est-ce vraiment pour y installer Tétris ou DragonBall ? S’il y avait un exemple de logiciel libre à ne pas présenter, c’était justement celui-là.</p>
<h3>Robots mobiles et autonomes contrôlés avec Pharo</h3>
<p>	Cette seconde conférence a été donnée par un enseignant-chercheur de l’école des Mines de Douai. Il y était question de robotique et d’un fork du langage Smalltalk appelé <a href="http://pharo-project.org/home">Pharo</a>. C’est un langage à objets et à typage dynamique, assez éloigné de ma culture informatique. Les possibilités ont l’air intéressantes, mais la conférence laisse un goût amer : les nombreuses démonstrations (une dizaine) manquaient de vigueur et donnaient l’impression qu’on travaille avec Pharo par ajouts de rustines successives sans jamais penser le programme dans sa globalité. Dommage, le potentiel était là.</p>
<h3>Autour de SSL/TSL</h3>
<p>	Sans doute la conférence que j’ai le plus apprécié. La sécurité informatique est aussi un domaine que je connais peu. Le conférencier a fait preuve de pédagogie avec des exemples variés et des schémas soignés. Au fil des diapos, on se demande quand même ce qu’Alice avait de si important à dire à Bob.</p>
<h3>Conclusion</h3>
<p>	Il serait peut-être intéressant de proposer, pour la session 2011, un atelier d’initiation à la programmation fonctionnelle à travers le langage OCaml. Cela permettrait de sortir des sentiers battus et de montrer que le typage statique fort est aussi capable de produire des outils puissants. Le problème avec les idées, c’est qu’on les lance sans réfléchir, et il n’y a presque jamais personne pour les rattraper avant qu’elles ne se brisent. <img src="https://blog.developpez.com/ocamlblog/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /></p>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Les questionnaires à choix multiples avec LaTeX</title>
		<link>https://blog.developpez.com/ocamlblog/p9437/biologie/les_questionnaires_a_choix_multiples_ave</link>
		<comments>https://blog.developpez.com/ocamlblog/p9437/biologie/les_questionnaires_a_choix_multiples_ave#comments</comments>
		<pubDate>Sun, 31 Oct 2010 16:31:13 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Biologie]]></category>
		<category><![CDATA[LaTeX]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Si vous avez des enseignants dans votre entourage, vous serez peut-être sollicités (si ce n&#8217;est déjà fait) pour créer des questionnaires à choix multiples (QCM). Les QCM constituent un outil commode pour évaluer des élèves ou des étudiants. Ils se présentent sous la forme de questions auxquelles plusieurs réponses possibles sont proposées. En général, une seule de ces réponses est correcte; les autres, appelées distracteurs, visent à éprouver la maîtrise du sujet par [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><br />
<table>
<tr>
<td><a href="http://fr.wikipedia.org/wiki/LaTeX" target="_blank"><img src="http://blog.developpez.com/media/LaTeX_logo.png" width="300" height="125" alt="" /></a></td>
</tr>
</table>
<p></center></p>
<p>Si vous avez des enseignants dans votre entourage, vous serez peut-être sollicités (si ce n&rsquo;est déjà fait) pour créer des <strong>questionnaires à choix multiples</strong> (QCM). Les QCM constituent un outil commode pour évaluer des élèves ou des étudiants. Ils se présentent sous la forme de questions auxquelles plusieurs réponses possibles sont proposées. En général, une seule de ces réponses est correcte; les autres, appelées distracteurs, visent à éprouver la maîtrise du sujet par les candidats. À ce titre, elles se doivent d&rsquo;être plausibles.</p>
<p><span id="more-22"></span></p>
<p>En règle générale, l&rsquo;évaluateur souhaite produire deux versions de son questionnaire : une version vierge destinée aux candidats, et une version corrigée, souvent destinée à l&rsquo;impression sur transparent, pour l&rsquo;aider dans la correction des copies. Nous allons voir qu&rsquo;il est possible de satisfaire à toutes ces exigences en utilisant LaTeX sans aucune extension dédiée !</p>
<h3>Des paquets</h3>
<p>Comme toujours, on commence un document LaTeX par l&rsquo;écriture de son préambule. Dans le cas qui nous intéresse, nous allons rédiger un questionnaire en français à l&rsquo;aide de la classe article. Nous avons donc besoin du code suivant : </p>
<pre>\documentclass[11pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage[T1]{fontenc}
\usepackage{lmodern}</pre>
<p>Les cases à cocher seront représentées par des symboles mathématiques, de sorte que nous devons aussi charger les paquets fournis par la <a href="http://fr.wikipedia.org/wiki/American_Mathematical_Society">société mathématique américaine</a> :</p>
<pre>\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}</pre>
<p>Nous souhaitons en outre utiliser tout l&rsquo;espace disponible afin de réduire la longueur de l&rsquo;énoncé. Nous souhaitons donc écrire nos questions sur plusieurs colonnes (paquet <code class="codecolorer text default"><span class="text">multicol</span></code>). Nous aurons aussi besoin de personnaliser l&rsquo;en-tête et le pied de page (paquet <code class="codecolorer text default"><span class="text">fancyhdr</span></code>)&#8230; et vous verrez plus tard que les macros que nous allons écrire nécessitent le paquet <code class="codecolorer text default"><span class="text">ifthen</span></code> :</p>
<pre>\usepackage{ifthen}
\usepackage{multicol}
\usepackage{fancyhdr} </pre>
<h3>Des macros</h3>
<p>Afin de pouvoir passer facilement du mode &laquo;&nbsp;énoncé&nbsp;&raquo; au mode &laquo;&nbsp;corrigé&nbsp;&raquo;, nous allons définir une variable booléenne <code class="codecolorer text default"><span class="text">correction</span></code> et l&rsquo;initialiser à faux (<code class="codecolorer text default"><span class="text">false</span></code>) :</p>
<pre>\newboolean{correction}
\setboolean{correction}{false}</pre>
<p>Nous allons aussi définir un compteur pour les numéros des questions :</p>
<pre>\newcounter{QNumber}</pre>
<h4>Définir une question</h4>
<p>Nous allons ensuite écrire une macro <code class="codecolorer text default"><span class="text">\Question</span></code> qui permet de définir une nouvelle <em>question</em>. Cette macro reçoit deux arguments : le premier, facultatif, indique quel est le caractère à placer en fin de question (le plus souvent &lsquo;:&rsquo;, mais on peut aussi trouver un point d&rsquo;interrogation), et le second n&rsquo;est autre que la question elle-même. Nous écrivons donc :</p>
<pre>\newcommand{\Question}[2][:]{
  \stepcounter{QNumber}
  \noindent\textbf{Question \theQNumber} -- #2~#1
}</pre>
<h4>Ajouter une liste de choix</h4>
<p>Nous allons aussi définir un environnement <code class="codecolorer text default"><span class="text">Reponse</span></code> pour permettre d&rsquo;entrer les différents choix proposés à la sagacité des candidats :</p>
<pre>\newenvironment{Reponse}{
  \begin{list}{$\square$}{\leftmargin=5em}
}{
  \end{list}\vspace{1em}
}</pre>
<h4>Proposition correcte et distracteurs</h4>
<p>Il ne nous reste plus qu&rsquo;à définir la macro <code class="codecolorer text default"><span class="text">\Vrai</span></code> (resp. <code class="codecolorer text default"><span class="text">\Faux</span></code>) pour ajouter une proposition vraie (resp. fausse) :</p>
<pre>\newcommand{\Vrai}{
  \item[\ifthenelse{\boolean{correction}}{$\blacksquare$}{$\square$}]}
\newcommand{\Faux}{\item[$\square$]}</pre>
<p>Vous remarquerez que le symbole utilisé par la macro \Vrai dépend de l&rsquo;état de la variable booléenne correction. De cette façon, si correction est égal à true, les réponses justes seront marquées par un carré plein, ce qui révèle la correction du questionnaire.</p>
<h3>En-tête et pied de page</h3>
<p>On souhaite ajouter en en-tête de la première page des champs permettant au condidat d&rsquo;indiquer son nom et son prénom :</p>
<pre>\chead{\ifthenelse{\thepage=1}{
  \textbf{Nom :}
  \makebox[12em]{\dotfill}
  \hspace{2em}
  \textbf{Prénom :}
  \makebox[12em]{\dotfill}}{}} </pre>
<p>Si les questions continuent au verso, on veut que le recto de la première page se termine par la mention &laquo;&nbsp;<em>Tournez la page s&rsquo;il vous plaît</em>&nbsp;&raquo; :  </p>
<pre>\rfoot{\ifthenelse{\thepage=1}{\textit{(tournez la page s.v.p)}}{}}</pre>
<p>Je laisse au lecteur le soin de généraliser ceci à toutes les pages impaires dans le cas d&rsquo;un questionnaire comportant plus de deux pages pleines.</p>
<h3>Compilation du document</h3>
<p>Pour compiler votre document et produire un fichier PDF, il vous suffit d&rsquo;utiliser la commande <strong>pdflatex qcm.tex</strong>. En changeant la valeur du booléen <code class="codecolorer text default"><span class="text">correction</span></code>, vous obtenez ainsi un <a href="http://www.box.net/shared/dj68ol7bms">énoncé</a> et un <a href="http://www.box.net/shared/zgqc9c5nlq">corrigé</a>. <a href="http://pastebin.com/fpFpPJZa">Le code source est disponible ici</a>.</p>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Les fonctions callback dans LablGTK</title>
		<link>https://blog.developpez.com/ocamlblog/p9294/informatique/les_fonctions_callback_dans_lablgtk</link>
		<comments>https://blog.developpez.com/ocamlblog/p9294/informatique/les_fonctions_callback_dans_lablgtk#comments</comments>
		<pubDate>Fri, 17 Sep 2010 07:01:47 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Après un long moment d&#8217;absence et de sérieux doutes sur la pérennité de ce blog, je reviens vous parler de LablGTK. Cette fois-ci, je ne vous présenterai pas un autre module de la bibliothèque. Pour célébrer la renaissance de ce blog, à la suite de mon admission en thèse (dans un domaine très éloigné du sujet de ce blog), je vous propose de parler du mécanisme de callback dans LablGTK. Nous allons aborder [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center></p>
<table>
<tr>
<td><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></td>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>Après un long moment d&rsquo;absence et de sérieux doutes sur la pérennité de ce blog, je reviens vous parler de LablGTK. Cette fois-ci, je ne vous présenterai pas un autre module de la bibliothèque. Pour célébrer la renaissance de ce blog, à la suite de mon admission en thèse (dans un domaine très éloigné du sujet de ce blog), je vous propose de parler du mécanisme de callback dans LablGTK.</p>
<p><span id="more-18"></span></p>
<p>Nous allons aborder dans ce billet une manière <em>automatisée</em> d&rsquo;associer des fonctions callback à des widgets. Mais, pour partir sur de bonnes bases, nous allons d&rsquo;abord rappeler ce qu&rsquo;est une fonction callback.</p>
<h3>Le principe d&rsquo;une fonction callback</h3>
<p>Une fonction callback (je ne me risque pas à proposer de traduction) est une fonction qui est communiquée dans un argument passé à une autre fonction. Dans le cas de GTK, un widget interagit avec l&rsquo;utilisateur en émettant des événements auxquels des fonctions peuvent être associées. Ces dernières, on l&rsquo;aura compris, ont pour but de répondre à l&rsquo;événement émis, et donc <em>in fine</em> à l&rsquo;utilisateur. Par exemple, le code suivant est typique :</p>
<pre><strong>let</strong> main_window =
  <strong>let</strong> window = GWindow.window
    ~title:"LablGTK demo"
    ~position:`CENTER
    ~width:640 ~height:480 () <strong>in</strong>
  window#connect#destroy ~callback:GMain.quit;
  window </pre>
<p>Ce code crée la fenêtre principale d&rsquo;une application. On y indique également qu&rsquo;il faut <em>fermer l&rsquo;application</em> (<code class="codecolorer text default"><span class="text">GMain.quit</span></code>) lorsque la fenêtre est détruite (ce qui se traduit par la levée de l&rsquo;événement <code class="codecolorer text default"><span class="text">destroy</span></code>). Dans ce cas, la fonction <code class="codecolorer text default"><span class="text">GMain.quit</span></code> est utilisée comme callback.</p>
<h3>Séparation de l&rsquo;interface et du code</h3>
<p>Pour construire une application, on recommande généralement de dissocier les éléments de l&rsquo;interface et les actions associées. Le code est ainsi mieux structuré et plus facile à maintenir. Par exemple, on peut considérer un module <code class="codecolorer text default"><span class="text">GUI</span></code> qui contient l&rsquo;ensemble des widgets de l&rsquo;interface, et divers modules qui définissent les actions associées (copie, collage, zoom, enregistrement, etc.). Malgré des qualités indéniables, cette approche présente au moins deux inconvénients de taille :</p>
<ul>
<li>Chaque fois que l&rsquo;interface est modifiée (par exemple en ajoutant ou en supprimant un widget), <em>le module qui définit l&rsquo;action correspondante doit être mis à jour</em> pour ajouter ou supprimer un enregistrement de fonction callback et tenir ainsi compte des modifications réalisées en amont.</li>
<li><em>Le module qui définit les actions n&rsquo;est plus autonome</em> : il dépend du module d&rsquo;interface de l&rsquo;application, de sorte que sa réutilisation dans un autre contexte devient plus difficile.</li>
</ul>
<p>Nous allons présenter ici une solution qui résout ces deux problèmes et permet d&rsquo;automatiser l&rsquo;enregistrement des fonctions callback.</p>
<h3>Automatisation du mécanisme de callback</h3>
<h4>Quelques nouveaux types</h4>
<p>Pour automatiser le mécanisme d&rsquo;enregistrement des fonctions callback, nous allons commencer par définir des types qui correspondent à une action particulière, comme l&rsquo;activation d&rsquo;un élément ou un clic gauche de souris. Ce sont :</p>
<pre><strong>class type</strong> clickable = 
  <strong>object</strong>
    <strong>method</strong> clicked : callback:(unit -> unit) -> GtkSignal.id
  <strong>end</strong>

<strong>class type</strong> activatable = 
  <strong>object</strong>
    <strong>method</strong> activate : callback:(unit -> unit) -> GtkSignal.id
  <strong>end</strong>
</pre>
<p>Notez que l&rsquo;on pourrait écrire de très nombreux autres types de ce genre, mais ceux-ci sont les plus fréquents car ils couvrent à la fois les barres d&rsquo;outils et les menus, c&rsquo;est-à-dire les éléments les plus susceptibles d&rsquo;être modifiés dans une interface (on peut aussi voir dans ces définitions, quoique d&rsquo;assez loin, une influence des typeclass de Haskell). On va ensuite définir un type somme pour réunir le tout :</p>
<pre><strong>type</strong> item =
  | C <strong>of</strong> clickable
  | A <strong>of</strong> activatable</pre>
<h4>Stockage des couples identifiant/données</h4>
<p>Nous allons ensuite définir une table de hachage destinée à stocker des identifiants (type <code class="codecolorer text default"><span class="text">string</span></code>) et des données associées. Ces données sont constituées d&rsquo;une liste d&rsquo;objets (<code class="codecolorer text default"><span class="text">item list</span></code>) et d&rsquo;une fonction de callback (de type <code class="codecolorer text default"><span class="text">unit -&gt; unit</span></code>). Comme nous voulons modifier ces valeurs directement, nous allons les stocker sous forme de références :</p>
<pre><strong>val</strong> table : (string, action list ref * (unit -> unit) ref) Hashtbl.t</pre>
<p>Nous allons maintenant définir une fonction <code class="codecolorer text default"><span class="text">register_any</span></code> qui, étant donné une fonction <code class="codecolorer text default"><span class="text">f</span></code>, un identifiant <code class="codecolorer text default"><span class="text">id</span></code> et un widget <code class="codecolorer text default"><span class="text">wid</span></code>, ajoute <code class="codecolorer text default"><span class="text">f wid</span></code> (de type <code class="codecolorer text default"><span class="text">item</span></code>) à la liste des objets associés à l&rsquo;identifiant <code class="codecolorer text default"><span class="text">id</span></code> :</p>
<pre><strong>let</strong> register_any f id wid =
  <strong>let</strong> item = f wid <strong>in</strong>
  <strong>try</strong>
    <strong>let</strong> items = fst (Hashtbl.find table id) <strong>in</strong>
    items := item :: !items
  <strong>with</strong> Not_found -> Hashtbl.add table id (ref [item], ref ignore)</pre>
<p><strong>Remarque</strong> : vous avez peut-être remarqué que, si l&rsquo;identifiant n&rsquo;existe pas dans la table de hachage, les données ajoutées comportent une fonction callback qui ne fait rien (<code class="codecolorer text default"><span class="text">Pervasives.ignore</span></code>). Elle pourra bien sûr être modifiée par la suite !</p>
<h4>Enregistrement des widgets</h4>
<p>Nous pouvons ensuite définir des fonctions spécialisées pour les deux types <code class="codecolorer text default"><span class="text">clickable</span></code> et <code class="codecolorer text default"><span class="text">activatable</span></code> définis précédemment :</p>
<pre><strong>let</strong> register_clickable ~id obj = 
  register_any (<strong>fun</strong> obj -> C (obj#connect :> clickable)) id obj

<strong>let</strong> register_activatable ~id obj = 
  register_any (<strong>fun</strong> obj -> A (obj#connect :> activatable)) id obj</pre>
<p>Ces fonctions ont la signature suivante :</p>
<pre><strong>val</strong> register_clickable : id:string -> <connect : #clickable; ..> -> unit</pre>
<pre><strong>val</strong> register_activatable : id:string -> <connect : #activatable; ..> -> unit</pre>
<p>En d&rsquo;autres termes, elles reçoivent en entrée un identifiant de type <code class="codecolorer text default"><span class="text">string</span></code> et un widget dont la méthode <code class="codecolorer text default"><span class="text">connect</span></code> est un sur-ensemble de la classe <code class="codecolorer text default"><span class="text">clickable</span></code> (ou <code class="codecolorer text default"><span class="text">activatable</span></code>, respectivement).</p>
<h4>Enregistrement des fonctions de callback</h4>
<p>Il faut ensuite écrire une fonction capable d&rsquo;enregistrer les fonctions callback. Pour simplifier son utilisation, on souhaite qu&rsquo;elle renvoie en sortie la fonction reçue en entrée :</p>
<pre><strong>let</strong> register_callback ~id f =
  <strong>try</strong> snd (Hashtbl.find table id) := f; f <strong>with</strong> Not_found -> f</pre>
<p>Cette fonction est de type :</p>
<pre><strong>val</strong> register_callback : id:string -> (unit -> unit) -> unit -> unit</pre>
<h4>Association des fonctions aux widgets</h4>
<p>Il ne reste plus qu&rsquo;à définir la fonction qui associe les callback au lancement de l&rsquo;application :</p>
<pre><strong>let</strong> connect_all () =
  Hashtbl.iter (<strong>fun</strong> _ ({contents = t}, {contents = f}) ->
    List.iter (<strong>function</strong>
      | C connect -> ignore (connect#clicked ~callback:f)
      | A connect -> ignore (connect#activate ~callback:f)
    ) t
  ) table</pre>
<p><strong>Rappel</strong> : en OCaml, les références peuvent être vues comme des valeurs de type record avec un champ unique appelé <code class="codecolorer text default"><span class="text">contents</span></code>. On peut donc filtrer les références en écrivant <code class="codecolorer text default"><span class="text">{contents = x}</span></code>, où <code class="codecolorer text default"><span class="text">x</span></code> désigne le contenu de la référence.</p>
<p>Cette fonction présente une signature très simple :</p>
<pre><strong>val</strong> connect_all : unit -> unit</pre>
<h3>Utilisation du module dans le programme</h3>
<p>L&rsquo;utilisation de ce module dans un programme se déroule en trois temps :</p>
<ul>
<li>Tout d&rsquo;abord, les éléments de l&rsquo;interface sont créés et enregistrés avec les fonctions <code class="codecolorer text default"><span class="text">register_clickable</span></code> ou <code class="codecolorer text default"><span class="text">register_activatable</span></code>. À cet effet, un identifiant spécifique est nécessaire&#8230; mais celui-ci peut être recyclé avantageusement et servir pour les plugins, voire pour la traduction de l&rsquo;application en plusieurs langues.</li>
<li>Ensuite, les fonctions callback sont enregistrées avec <code class="codecolorer text default"><span class="text">register_callback</span></code> et le même identifiant que précédemment. Les modules qui les contiennent restent indépendants du module d&rsquo;interface. Accessoirement, les équipes de développeurs qui se parlent peu auront moins de surprises lors de la mise en commun de leur code ;-).</li>
<li>Enfin, au lancement de l&rsquo;application, on connecte tous les widgets à leurs fonctions respectives à l&rsquo;aide d&rsquo;un unique appel à <code class="codecolorer text default"><span class="text">connect_all</span></code>. On n&rsquo;oublie personne !</li>
</ul>
<p>On peut bien entendu imaginer diverses généralisations plus ou moins complexes de ce procédé, notamment au cas des fonctions callback multiples. On aurait alors une table de hachage de type :</p>
<pre><strong>val</strong> table : (string, action list ref * (unit -> unit) list ref) Hashtbl.t</pre>
<p>et la fonction <code class="codecolorer text default"><span class="text">connect_all</span></code> deviendrait :</p>
<pre>let connect_all () =
  Hashtbl.iter (<strong>fun</strong> _ ({contents = obj_list}, {contents = fun_list}) ->
    List.iter (<strong>fun</strong> item ->
      <strong>let</strong> f = <strong>match</strong> item <strong>with</strong>
        | C connect -> (<strong>fun</strong> f -> ignore (connect#clicked ~callback:f))
        | A connect -> (<strong>fun</strong> f -> ignore (connect#activate ~callback:f))
      <strong>in</strong> List.iter f fun_list
    ) obj_list
  ) table</pre>
<p><strong>Remarque</strong> : il commence à y avoir beaucoup d&rsquo;itérateurs imbriqués. Il faut bien voir qu&rsquo;il s&rsquo;agit d&rsquo;appliquer les fonctions associées à l&rsquo;identifiant <code class="codecolorer text default"><span class="text">id</span></code> à <em>chacun</em> des éléments enregistrés.</p>
<h3>En conclusion&#8230;</h3>
<p>D&rsquo;abord, un lien vers le <a href="http://ocaml.pastebin.com/P6fnJVpW"><strong>code complet</strong></a> et l&rsquo;<a href="http://ocaml.pastebin.com/WBC4Zgc3"><strong>interface complète</strong></a>.</p>
<p>Il est certain que l&rsquo;intérêt d&rsquo;un tel module se ressent surtout dans de moyennes ou grosses applications, dont l&rsquo;interface est complexe et présente un certain niveau de redondance. Je ne pense pas qu&rsquo;il soit raisonnable de l&rsquo;utiliser pour écrire un pong.</p>
<p>Voilà, c&rsquo;est tout pour ajourd&rsquo;hui !</p>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Plus d&#8217;encre.</title>
		<link>https://blog.developpez.com/ocamlblog/p8797/actualites/plus_d_encre</link>
		<comments>https://blog.developpez.com/ocamlblog/p8797/actualites/plus_d_encre#comments</comments>
		<pubDate>Tue, 06 Apr 2010 19:18:11 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Actualités]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Amateurs de dromadaires, férus d&#8217;informatique, caméliens, lecteurs en tout genre, bonsoir ! Le temps me manque plus cruellement que jamais pour alimenter ce blog. Pourtant, ce ne sont pas les sujets qui manquent. L&#8217;actualité, à elle seule, apporte matière à des dizaines de billets. Un billet, par exemple, pourrait évoquer le décès récent de Robin Milner et retracer son brillant parcours d&#8217;inventeur du langage ML et coauteur de l&#8217;algorithme d&#8217;inférence qui porte désormais son nom, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><strong>Amateurs de dromadaires, férus d&rsquo;informatique, caméliens, lecteurs en tout genre, bonsoir !</strong></p>
<p>Le temps me manque plus cruellement que jamais pour alimenter ce blog. Pourtant, ce ne sont pas les sujets qui manquent. L&rsquo;actualité, à elle seule, apporte matière à des dizaines de billets. </p>
<p>Un billet, par exemple, pourrait évoquer le décès récent de <a href="http://fr.wikipedia.org/wiki/Robin_Milner" target="_blank">Robin Milner</a> et retracer son brillant parcours d&rsquo;inventeur du langage ML et coauteur de l&rsquo;algorithme d&rsquo;inférence qui porte désormais son nom, l&rsquo;algorithme de Hindley-Milner.</p>
<p>Les temps actuels sont aussi enrichissants en dehors du monde informatique. Les débats de société récents sur les changements climatiques et les biotechnologies soulèvent des questions scientifiques et philosophiques qui mériteraient de longs développements. Hélas, je n&rsquo;ai plus le temps pour ça.</p>
<p>Peut-être le blog réouvrera-t-il ses portes un jour&#8230; mais pour l&rsquo;heure, rideau ! J&rsquo;adresse mes sincères remerciements à tous les lecteurs qui ont suivi ces billets avec intérêt.</p>
<p>Bien cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Aller simple pour le pays des fantômes</title>
		<link>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes</link>
		<comments>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes#comments</comments>
		<pubDate>Fri, 19 Feb 2010 20:04:04 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Biologie]]></category>
		<category><![CDATA[Camlimages]]></category>
		<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Après un long moment d&#8217;absence, je reviens pour vous parler d&#8217;une notion dont l&#8217;intérêt m&#8217;a été révélé par un article sur le blog de bluestorm. Il s&#8217;agit des types fantômes, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&#8217;un cas concret autour de ce que je développe actuellement. Le contexte Une application pour microscopistes Je suis actuellement en train d&#8217;écrire les grandes lignes d&#8217;un programme destiné à des microscopistes amateurs, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><center></p>
<table>
<tr>
<td><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></td>
<td><a href="http://pauillac.inria.fr/camlimages/" target="_blank"><img src="http://pauillac.inria.fr/camlimages/fumicaml2-small.jpg"/></a></td>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>Après un long moment d&rsquo;absence, je reviens pour vous parler d&rsquo;une notion dont l&rsquo;intérêt m&rsquo;a été révélé par un article sur le blog de <a href="http://blog.huoc.org/7-en-bref-aussi.html" target="_blank">bluestorm</a>. Il s&rsquo;agit des <strong>types fantômes</strong>, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&rsquo;un cas concret autour de ce que je développe actuellement.</p>
<p><span id="more-7"></span></p>
<h3>Le contexte</h3>
<h4>Une application pour microscopistes</h4>
<p>Je suis actuellement en train d&rsquo;écrire les grandes lignes d&rsquo;un programme destiné à des microscopistes amateurs, dont il existe déjà une version en Visual Basic (à réécrire et faire évoluer). Ce programme doit permettre de mettre en forme des images pour un forum en leur ajoutant un cartouche contenant diverses informations utiles (échelle, type de matériel, coloration éventuelle, etc.). Je vous donne une image ci-dessous pour vous faire une idée. Une caractéristique importante de ce logiciel réside dans sa capacité à dessiner des échelles à partir d&rsquo;une relation entre un nombre X de pixels et une taille Y en micromètres, déterminée sur une image étalon.</p>
<p><center><img src="http://blog.developpez.com/media/Helvella_leucomelaena-1_small.jpg" width="800" height="600" alt="" /><br />
<strong>Spores d&rsquo;helvelle après coloration.<br />
Objectif Zeiss Planapo 100/1.3</strong><br />
</center></p>
<h4>Image d&rsquo;étalon</h4>
<p>Une image étalon est une photo dont les éléments ont une taille connue, prise dans les mêmes conditions qu&rsquo;une image classique. Le plus souvent, il s&rsquo;agit d&rsquo;une « règle » dont les graduations sont espacées de 10 µm (la bagatelle de 100 graduations dans un millimètre&#8230;). Le programme charge l&rsquo;image d&rsquo;étalon, l&rsquo;analyse à la manière d&rsquo;un outil de reconnaissance de caractères (en beaucoup plus simple !), et en déduit la taille d&rsquo;une graduation, exprimée en pixels. De son côté, l&rsquo;utilisateur indique la taille réelle correspondante, en micromètres cette fois-ci <em>(cf. l&rsquo;image ci-dessous)</em>.</p>
<p><center><img src="http://blog.developpez.com/media/IMG_0029_small.JPG" width="800" height="533" alt="Micromètre" /><br />
<strong>Image d&rsquo;étalon (1 graduation correspond à 10 µm).<br />
Objectif Zeiss Plan Neofluar 63/1.25</strong><br />
</center></p>
<h3>Manipulation d&rsquo;images</h3>
<p>Pour automatiser la reconnaissance des graduations, on souhaite implémenter l&rsquo;algorithme suivant (plutôt une recette de cuisine) :</p>
<ul>
<li>Charger l&rsquo;image en couleur (24 bits).</li>
<li>La transformer en niveaux de gris.</li>
<li>Augmenter la luminosité pour éclaircir le fond.</li>
<li>Passer en noir et blanc.</li>
<li>Effacer les poussières pour uniformiser le fond.</li>
<li>Reboucher les trous dans les traits de graduations.</li>
<li>Mesurer les traits de graduation et les espaces qu&rsquo;ils délimitent.</li>
<li>En déduire la taille d&rsquo;une graduation (un espace + un trait).</li>
</ul>
<p>Pour y parvenir, nous allons utiliser <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html">LablGTK</a> et <a href="http://pauillac.inria.fr/camlimages/">CamlImages</a>, deux bibliothèques qui dialoguent plutôt bien. Nous allons nous fixer comme objectif d&rsquo;écrire un type <code class="codecolorer text default"><span class="text">picture</span></code> polymorphe. Nous nous en servirons pour distinguer les images en couleur (24 bits), en niveaux de gris et en noir et blanc. Les fonctions telles que <code class="codecolorer text default"><span class="text">get_width</span></code> ou <code class="codecolorer text default"><span class="text">get_pixbuf</span></code> s&rsquo;appliqueront à tout type d&rsquo;image. </p>
<h3>Choix d&rsquo;une interface</h3>
<p>Pour faire court, nous voudrions quelque chose comme ceci :</p>
<pre><strong>type</strong> 'a picture

<strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom

<strong>val</strong> from_file : string -> fullcolor picture
<em>(** Charge une image en couleurs. *)</em>

<em>(** Fonctions usuelles, valables pour tout type d'image. *)</em>
<strong>val</strong> copy : 'a picture -> 'a picture
<strong>val</strong> get_width : 'a picture -> int
<strong>val</strong> get_height : 'a picture -> int
<strong>val</strong> get_pixbuf : 'a picture -> GdkPixbuf.pixbuf

<strong>val</strong> grayscale : 
  ?filter:(Color.rgb -> int) -> 
  fullcolor picture -> grayscale picture
<em>(** Conversion en niveaux de gris d'une image couleur (24 bits). *)</em>

<strong>val</strong> black_and_white :
  ?threshold:int ->
  grayscale picture -> monochrom picture
<em>(** Conversion en noir et blanc d'une image en niveaux de gris. *)</em>

<strong>val</strong> noise_reduction :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Effacement des poussières sur une image en noir et blanc. *)</em>

<strong>val</strong> fill_gaps :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Rebouchage des trous sur une image en noir et blanc. *)</em></pre>
<p>Pour que cet algorithme ne reste pas nébuleux, voici <a href="http://www.box.net/shared/88aynv0ory" target="_blank">un PDF d&rsquo;illustration</a>.</p>
<h3>Du côté de l&rsquo;implémentation</h3>
<p>Après l&rsquo;interface, voyons l&rsquo;implémentation. <em>A priori</em>, cela peut sembler difficile. Pourtant, grâce aux types fantômes, nous n&rsquo;avons presque rien à faire ! Définissons d&rsquo;abord le type <code class="codecolorer text default"><span class="text">picture</span></code> :</p>
<pre><strong>type</strong> 'a picture = {pict: OImages.rgb24_class}</pre>
<p>Ne vous étonnez pas de trouver ici un record alors qu&rsquo;il paraît inutile. Le vrai type <code class="codecolorer text default"><span class="text">picture</span></code>, que j&rsquo;utilise dans mon application, contient d&rsquo;autres champs (mais <strong>aucun</strong> n&rsquo;est de type <code class="codecolorer text default"><span class="text">'a</span></code>, c&rsquo;est très important). Il a été simplifié ici par souci de pédagogie.</p>
<p>Définissons maintenant trois types fantômes :</p>
<pre><strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom</pre>
<p><font color="#000080">Un <strong>type fantôme</strong> est un <strong>type utilisé comme paramètre d&rsquo;un autre type</strong> (à la manière du type <code class="codecolorer text default"><span class="text">int</span></code> dans <code class="codecolorer text default"><span class="text">int list</span></code>), mais <strong>qui ne sert pas dans la définition de ce type</strong> (comme <code class="codecolorer text default"><span class="text">'a</span></code> dans <code class="codecolorer text default"><span class="text">'a picture</span></code> ci-dessus). Le type fantôme vous permet d&rsquo;ajouter une information (une <em>contrainte</em>) qui sera propagée correctement par l&rsquo;algorithme d&rsquo;inférence de types, sans impact sur l&rsquo;implémentation. Dans mon cas, il s&rsquo;agit surtout de s&rsquo;assurer que l&rsquo;on passe bien une image en niveaux de gris à la fonction <code class="codecolorer text default"><span class="text">black_and_white</span></code>, par exemple.</font></p>
<p>Nous pouvons maintenant écrire la fonction de création :</p>
<pre><strong>let</strong> from_file str = {pict = OImages.rgb24 (OImages.load path [])}</pre>
<p>puis les fonctions utilitaires :</p>
<pre><strong>let</strong> get_width t = t.pict#width
<strong>let</strong> get_height t = t.pict#height
<strong>let</strong> get_pixbuf t = Imagegdk.to_pixbuf t.pict#coerce
<strong>let</strong> copy t = {t with pict = Oo.copy t.pict}</pre>
<p>Nous pouvons maintenant en venir aux fonctions qui agissent directement sur l&rsquo;image et, comme vous allez le constater, on ne se doute de rien en lisant cette partie du code  :</p>
<pre><strong>let</strong> grayscale ?(filter = Color.brightness) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> res = filter (img#unsafe_get x y) <strong>in</strong>
      img#unsafe_set x y {Color.r = res; g = res; b = res}
    <strong>done</strong>
  <strong>done</strong>;
  res

<strong>let</strong> black = {Color.r = 0; g = 0; b = 0}
<strong>let</strong> white = {Color.r = 255; g = 255; b = 255}

<strong>let</strong> black_and_white ?(threshold = 0x90) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> rgb = img#unsafe_get x y <strong>in</strong>
      img#unsafe_set x y (<strong>if</strong> rgb.Color.r > threshold <strong>then</strong> white <strong>else</strong> black)
    <strong>done</strong>
  <strong>done</strong>;
  res</pre>
<p>et ainsi de suite (le détail d&rsquo;implémentation des fonctions <code class="codecolorer text default"><span class="text">&nbsp;</span></code> et <code class="codecolorer text default"><span class="text">fill_gaps</span></code> sort du cadre de ce billet; je peux développer sur demande, mais il ne me semble pas pertinent de le faire ici). Avec un appel à <code class="codecolorer text default"><span class="text">ocamlc -i</span></code>, toutes ces fonctions accepteraient des images de type <code class="codecolorer text default"><span class="text">'a picture</span></code>. La contrainte que nous avons posée dans l&rsquo;interface est à la fois valide et transparente du côté de l&rsquo;implémentation.</p>
<p>Il est intéressant de noter que les types fantômes apportent une information supplémentaire sans impact sur l&rsquo;implémentation. Cela renforce la sûreté du code sans nuire aux performances (imaginez la lourdeur qui consisterait à s&rsquo;assurer d&rsquo;abord que tous les pixels sont bien des teintes de gris !).</p>
<h3>En guise de conclusion&#8230;</h3>
<p>Je parlais d&rsquo;un <em>aller simple</em> vers le monde des fantômes. Vous savez maintenant pourquoi : quand on y a goûté, on ne peut plus s&rsquo;en passer. <strong>Les types fantômes, c&rsquo;est bien, mangez-en !</strong></p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Exemple d&#8217;utilisation du widget GtkSourceView 2.0</title>
		<link>https://blog.developpez.com/ocamlblog/p8057/informatique/title_145</link>
		<comments>https://blog.developpez.com/ocamlblog/p8057/informatique/title_145#comments</comments>
		<pubDate>Sun, 13 Sep 2009 18:35:16 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Je vous ai parlé, dans le précédent billet, de l&#8217;intégration du widget GtkSourceView 2.0 dans la bibliothèque LablGTK. Voyons ici ce qui change par rapport à l&#8217;ancien widget. Voici ci-dessous un petit code qui permet de voir quelques-unes des nouveautés de GtkSourceView 2.0, entre autres les thèmes (GtkStyleScheme) et la matérialisation des espaces. Un thème est choisi aléatoirement au lancement de l&#8217;application (évidemment, plus vous en avez et plus il y aura de [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Je vous ai parlé, dans le précédent billet, de l&rsquo;intégration du widget <strong>GtkSourceView 2.0</strong> dans la bibliothèque LablGTK. Voyons ici ce qui change par rapport à l&rsquo;ancien widget.</p>
<p><span id="more-13"></span></p>
<p>Voici ci-dessous un petit code qui permet de voir quelques-unes des nouveautés de <strong>GtkSourceView 2.0</strong>, entre autres les thèmes (<strong>GtkStyleScheme</strong>) et la matérialisation des espaces. Un thème est choisi aléatoirement au lancement de l&rsquo;application (évidemment, plus vous en avez et plus il y aura de visuels possibles&#8230;). Pour tester, placez le code suivant dans un fichier <strong>test.ml</strong>, puis compilez avec :</p>
<p><center>ocamlopt -w s -I +lablgtk2 lablgtk.cmxa lablgtksourceview2.cmxa test.ml -o test</center></p>
<p>Si vous voulez utiliser un nom différent, n&rsquo;oubliez pas de modifier la fonction <strong>read</strong> dans le code source !</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window 
    ~title:"GtkSourceView 2.0 Demo" 
    ~position:`CENTER 
    ~resizable:<strong>false</strong>  
    ~width:800 ~height:600 () in 
  wnd#connect#destroy GMain.quit; 
  wnd 
 
<strong>let</strong> ocaml = 
  <strong>let</strong> manager = GSourceView2.source_language_manager ~default:<strong>true</strong> <strong>in</strong> 
  <strong>match</strong> manager#language "objective-caml" <strong>with</strong> 
  | None -> failwith "La coloration syntaxique pour OCaml n'est pas disponible !" 
  | some -> some
 
<strong>let</strong> manager = GSourceView2.source_style_scheme_manager ~default:<strong>true</strong> 
<strong>let</strong> schemes = Array.of_list manager#style_scheme_ids 
 
<strong>let</strong> get_random_style_scheme () = 
  Random.self_init (); 
  <strong>match</strong> manager#style_scheme schemes.(Random.int (Array.length schemes)) <strong>with</strong> 
  | None -> <strong>assert false</strong> 
  | some -> some 
 
<strong>let</strong> scroll = GBin.scrolled_window 
  ~hpolicy:`ALWAYS 
  ~vpolicy:`ALWAYS 
  ~packing:window#add () 
 
<strong>let</strong> source = 
  <strong>let</strong> src = GSourceView2.source_view 
    ~draw_spaces:[`NEWLINE; `SPACE] <em>(* Matérialisation des espaces. *)</em> 
    ~auto_indent:<strong>true</strong> 
    ~indent_on_tab:<strong>true</strong> <em>(* Quelques étiquettes ont été renommées *)</em> 
    ~indent_width:2 
    ~insert_spaces_instead_of_tabs:<strong>true</strong> 
    ~right_margin_position:80 
    ~show_line_numbers:<strong>true</strong> 
    ~show_right_margin:<strong>true</strong> 
    ~packing:scroll#add () <strong>in</strong> 
  src#misc#modify_font_by_name "Monospace 10"; 
  <strong>let</strong> buf = src#source_buffer <strong>in</strong> 
  buf#set_language ocaml; 
  buf#set_highlight_syntax <strong>true</strong>; 
  buf#set_style_scheme (get_random_style_scheme ()); 
  src 
 
<strong>let</strong> read () = 
  <strong>let</strong> ich = open_in "test.ml" <strong>in</strong> 
  <strong>let</strong> len = in_channel_length ich <strong>in</strong> 
  <strong>let</strong> str = String.create len <strong>in</strong> 
  really_input ich str 0 len; 
  close_in ich; 
  Glib.Convert.locale_to_utf8 str 
 
<strong>let</strong> _ = 
  source#source_buffer#set_text (read ()); 
  window#show (); 
  GMain.main ()</pre>
<p>Ce code nous donne quelque chose comme ça :</p>
<p><center><br />
<img src="http://blog.developpez.com/media/GtkSourceView2.png" width="810" height="629" alt="" /><br />
</center></p>
<p>Le nouveau widget bénéficie d&rsquo;une interface plus agréable à utiliser, avec des étiquettes plus parlantes qu&rsquo;auparavant. Il ne lui manque plus que la factorisation du code (<em>code folding</em>) et l&rsquo;auto-complétion, qui sont sans doute les deux fonctionnalités les plus attendues pour les prochaines versions.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GPack</title>
		<link>https://blog.developpez.com/ocamlblog/p8552/informatique/lablgtk_le_module_gpack</link>
		<comments>https://blog.developpez.com/ocamlblog/p8552/informatique/lablgtk_le_module_gpack#comments</comments>
		<pubDate>Sat, 23 Jan 2010 12:57:10 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Je vous propose de poursuivre notre exploration de la bibliothèque LablGTK. Dans ce billet, nous allons parler du module GPack. Celui-ci contient tous les widgets nécessaires à la réalisation d&#8217;interfaces complexes. La plupart d&#8217;entre eux sont transparents pour l&#8217;utilisateur, car ils n&#8217;ont pas de représentation graphique. Pourtant, comme nous allons le voir ici, ce sont de précieux alliés ! Introduction Nous avons vu dans un précédent billet que l&#8217;élément principal d&#8217;une interface est [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Je vous propose de poursuivre notre exploration de la bibliothèque LablGTK. Dans ce billet, nous allons parler du module <code class="codecolorer text default"><span class="text">GPack</span></code>. Celui-ci contient tous les widgets nécessaires à la réalisation d&rsquo;interfaces complexes. La plupart d&rsquo;entre eux sont transparents pour l&rsquo;utilisateur, car ils n&rsquo;ont pas de représentation graphique. Pourtant, comme nous allons le voir ici, ce sont de précieux alliés !</p>
<p><span id="more-17"></span></p>
<h3>Introduction</h3>
<p>Nous avons vu dans <a href="http://blog.developpez.com/ocamlblog/p8477/programmation-fonctionnelle/lablgtk2/title-157/#more8477" target="_blank">un précédent billet</a> que l&rsquo;élément principal d&rsquo;une interface est généralement une fenêtre (GtkWindow). Or ce widget ne peut contenir qu&rsquo;<em>un seul</em> widget enfant. Cela semble, a priori, une limitation majeure. Pourtant, GTK permet de réaliser des interfaces arbitrairement complexes. Il est donc possible de lever cette limitation. Pour cela, GTK définit des conteneurs, c&rsquo;est-à-dire des widgets dont la fonction principale est de contenir d&rsquo;autres widgets. Ces conteneurs peuvent être imbriqués à souhait et constituent donc une solution à la fois élégante et souple.</p>
<h3>Les boîtes horizontales (<a href="http://library.gnome.org/devel/gtk/unstable/GtkHBox.html" target="_blank">GtkHBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-hbox_01.png" width="156" height="126" alt="" /></center></p>
<p>Ces boîtes permettent d&rsquo;insérer des widgets <strong>horizontalement</strong>. Les enfants sont insérés avec la méthode <code class="codecolorer text default"><span class="text">#add</span></code>, ou avec <code class="codecolorer text default"><span class="text">#pack</span></code> si l&rsquo;on souhaite paramétrer finement le style d&rsquo;insertion (entre autres, la capacité du widget enfant à être élargi pour remplir l&rsquo;espace disponible). Voyez par exemple :</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window ~width:150 ~height:100 () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> packing = 
  <strong>let</strong> hbox = GPack.hbox
    ~spacing:5 <em>(* Les enfants sont espacés de 5 pixels. *)</em>
    ~border_width:5 <em>(* La boîte possède une bordure de 5 pixels. *)</em>
    ~packing:window#add () <strong>in</strong>
  hbox#pack ~expand:<strong>false</strong>

<strong>let</strong> _ = 
  List.iter (<strong>fun</strong> text -> ignore (GMisc.label ~packing ~text ())) ["foo"; "bar"];
  window#show ();
  GMain.main ()</pre>
<p><strong>Pour info</strong> : cet exemple, comme les suivants, est complet et peut-être testé dans l&rsquo;interpréteur après avoir chargé la bibliothèque LablGTK.  </p>
<h3>Les boîtes verticales (<a href="http://library.gnome.org/devel/gtk/unstable/GtkVBox.html" target="_blank">GtkVBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-vbox.png" width="156" height="126" alt="" /></center></p>
<p>Les boîtes verticales fonctionnent de la même manière que les boîtes horizontales, à un détail près : les widgets enfants sont insérés <strong>verticalement</strong>.</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window ~width:150 ~height:100 () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> packing = 
  <strong>let</strong> vbox = GPack.vbox
    ~spacing:5 <em>(* Les enfants sont espacés de 5 pixels. *)</em>
    ~border_width:5 <em>(* La boîte possède une bordure de 5 pixels. *)</em>
    ~packing:window#add () <strong>in</strong>
  vbox#pack ~expand:<strong>false</strong>

<strong>let</strong> _ = 
  List.iter (<strong>fun</strong> text -> ignore (GMisc.label ~packing ~text ())) ["foo"; "bar"];
  window#show ();
  GMain.main ()</pre>
<h3>Les tableaux (<a href="http://library.gnome.org/devel/gtk/unstable/GtkTable.html" target="_blank">GtkTable</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-table.png" width="332" height="227" alt="" /></center></p>
<p>Les tableaux (GtkTable) sont un moyen pratique d&rsquo;organiser des widgets sous forme de tableaux (on s&rsquo;en serait douté&#8230;). Ils peuvent être utilisés, par exemple, pour créer des palettes. Les widgets sont insérés à l&rsquo;aide de la méthode <code class="codecolorer text default"><span class="text">#attach</span></code> en indiquant l&rsquo;emplacement des coins gauche (<code class="codecolorer text default"><span class="text">left</span></code>) et haut (<code class="codecolorer text default"><span class="text">top</span></code>) du widget.</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> attach = 
  <strong>let</strong> table = GPack.table 
    ~rows:4 <em>(* Nombre de rangées. *)</em>
    ~columns:4 <em>(* Nombre de colonnes. *)</em>
    ~row_spacings:2 <em>(* Espacement des éléments d'une même rangée. *)</em>
    ~col_spacings:2 <em>(* Espacement des éléments d'une même colonne. *)</em>
    ~packing:window#add () <strong>in</strong>
  <strong>fun</strong> i -> table#attach ~left:(i <strong>mod</strong> 4) ~top:(i / 4)

<strong>let</strong> _ = 
  Random.self_init ();
  <strong>for</strong> i = 0 <strong>to</strong> 24 <strong>do</strong>
    <strong>let</strong> r = Random.int 256 <strong>and</strong> g = Random.int 256 <strong>and</strong> b = Random.int 256 <strong>in</strong>
    <strong>let</strong> text = Printf.sprintf "#%02x%02x%02x" r g b <strong>in</strong>
    <strong>let</strong> entry = GEdit.entry ~text ~width:80 ~packing:(attach i) () <strong>in</strong>
    entry#misc#modify_base [`NORMAL, `NAME text]
  <strong>done</strong>;
  window#show ();
  GMain.main ()</pre>
<h3>Les boîtes pour boutons (<a href="http://library.gnome.org/devel/gtk/unstable/GtkButtonBox.html" target="_blank">GtkButtonBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-button_box.png" width="306" height="176" alt="" /></center></p>
<p>GTK fournit une boîte spécialement conçue pour recevoir des boutons : il s&rsquo;agit de GtkButtonBox, ou <code class="codecolorer text default"><span class="text">GPack.button_box</span></code> dans LablGTK. Elle est conçue pour vous permettre d&rsquo;organiser des boutons à la manière des boîtes de dialogue :</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window
    ~title:"Button box demo"
    ~position:`CENTER 
    ~resizable:<strong>false</strong>
    ~width:300 ~height:150 () in
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> vbox = GPack.vbox 
  ~spacing:2 
  ~border_width:2
  ~packing:window#add ()

<strong>let</strong> view = GText.view ~packing:vbox#add ()

<strong>let</strong> bbox = GPack.button_box `HORIZONTAL
  ~layout:`EDGE <em>(* C'est ici que l'on choisit la disposition des boutons. *)</em>
  ~border_width:2
  ~packing:(vbox#pack ~expand:<strong>false</strong>) ()

<strong>let</strong> help = GButton.button ~stock:`HELP ~packing:bbox#add ()
<strong>let</strong> quit = GButton.button ~stock:`QUIT ~packing:bbox#add ()

<strong>let</strong> _ =
  window#show ();
  GMain.main ()</pre>
<h3>Autres éléments</h3>
<p>Le module GPack définit d&rsquo;autres widgets, notamment les <strong>pages à onglets</strong> (<a href="http://library.gnome.org/devel/gtk/unstable/GtkNotebook.html" target="_blank">GtkNotebook</a>) et les <strong>panneaux mobiles</strong> (<a href="http://library.gnome.org/devel/gtk/unstable/GtkPaned.html" target="_blank">GtkPaned</a>). En raison de leur intérêt tout particulier dans la réalisation d&rsquo;interfaces, nous y consacrerons un billet entier.</p>
<h3>Atelier : Le triangle de Pascal</h3>
<p>Pour terminer, je vous propose de réaliser une petite application qui affiche à l&rsquo;écran les coefficients binomiaux en les présentant sous la forme du célèbre <a href="http://fr.wikipedia.org/wiki/Triangle_de_Pascal" target="_blank">triangle de Pascal</a>. Voici à quoi devra ressembler notre application :</p>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-atelier-triangle-Pascal.png" width="406" height="326" alt="" /></center></p>
<p>Pour réaliser notre application, nous avons besoin d&rsquo;une fenêtre :</p>
<pre><strong>let</strong> window =
  <strong>let</strong> wnd = GWindow.window
    ~title:"GPack demo"
    ~position:`CENTER
    ~resizable:<strong>false</strong>
    ~width:400 ~height:300 () in
  wnd#connect#destroy GMain.quit;
  wnd</pre>
<p>Celle-ci doit recevoir deux éléments distincts : une fenêtre assortie de barres de défilement, pour recevoir le triangle, et un bouton de fermeture. Nous allons donc commencer par insérer une boîte verticale dans <code class="codecolorer text default"><span class="text">window</span></code> :</p>
<pre><strong>let</strong> vbox = GPack.vbox 
  ~border_width:2 
  ~spacing:2 
  ~packing:window#add ()</pre>
<p>Nous pouvons alors ajouter les barres de défilement pour permettre à l&rsquo;utilisateur d&rsquo;explorer les lignes du triangle :</p>
<pre><strong>let</strong> scroll = GBin.scrolled_window
  ~hpolicy:`ALWAYS
  ~vpolicy:`ALWAYS
  ~packing:window#add ()</pre>
<p>Ces barres de défilement ajoutées avec <code class="codecolorer text default"><span class="text">GBin.scrolled_window</span></code> ne résolvent pas le problème de <code class="codecolorer text default"><span class="text">GWindow.window</span></code>, car l&rsquo;une comme l&rsquo;autre ne peuvent contenir qu&rsquo;un seul widget enfant. Pour lever cette limitation, nous devons donc insérer une boîte verticale à l&rsquo;intérieur des barres de défilement. Ici, comme il doit être possible de faire défiler le contenu de la boîte, on utilise la méthode <code class="codecolorer text default"><span class="text">#add_with_viewport</span></code> au lieu du classique <code class="codecolorer text default"><span class="text">#add</span></code>. Ce qui nous donne :</p>
<pre><strong>let</strong> vbox, packing = 
  <strong>let</strong> vbox = GPack.vbox
    ~spacing:2
    ~packing:scroll#add_with_viewport () <strong>in</strong>
  vbox, vbox#pack ~expand:<strong>false</strong></pre>
<p>Écrivons maintenant une fonction spécialisée (par application partielle) de <code class="codecolorer text default"><span class="text">GMisc.label</span></code> pour nous simplifier la tâche :</p>
<pre><strong>let</strong> label = GMisc.label ~height:50 ~width:50 ~xalign:0.5</pre>
<p>Pour information, les paramètres <code class="codecolorer text default"><span class="text">height</span></code> et <code class="codecolorer text default"><span class="text">width</span></code> fixent la taille des étiquettes produites (pas étonnant quand même&#8230;), et <code class="codecolorer text default"><span class="text">xalign</span></code> détermine l&rsquo;<strong>alignement</strong> du texte (aligné à gauche avec 0.0, centré avec 0.5 et aligné à droite avec 1.0).</p>
<p>Venons-en maintenant au coeur du problème. Nous devons écrire une fonction qui affiche les lignes successives du triangle de Pascal en calculant les coefficients. Ici, les boucles sont pratiques, je préfère donc donner une version en style impératif. Les principales étapes sont les suivantes :</p>
<ul>
<li>Pour tout entier i entre 1 et n, créer un nouveau conteneur horizontal (<code class="codecolorer text default"><span class="text">GPack.hbox</span></code>) et la fonction d&rsquo;empaquetage associée (<code class="codecolorer text default"><span class="text">packing</span></code>).</li>
<li>Pour tout entier j entre 1 et j, calculer le coefficient binomial, égal à 1 lorsque j = 1 ou j = i, et coeff(i &#8211; 1, j &#8211; 1) + coeff(i &#8211; 1, j) sinon. Le stocker dans une table de hachage pour réutilisation ultérieure (mémoïsation).</li>
<li>Créer une étiquette (<code class="codecolorer text default"><span class="text">GMisc.label</span></code>) contenant le coefficient, et l&rsquo;insérer dans le conteneur horizontal précédemment créé.</li>
</ul>
<p>Nous pouvons satisfaire à toutes ces exigences avec le code suivant :</p>
<pre><strong>let</strong> create n =
  <strong>let</strong> mem = Hashtbl.create n <strong>in</strong>
  <strong>let</strong> get = Hashtbl.find mem <strong>in</strong>
  <strong>for</strong> i = 1 <strong>to</strong> n <strong>do</strong>
    <strong>let</strong> packing = (GPack.hbox ~spacing:2 ~packing ())#pack ~expand:<strong>false</strong> <strong>in</strong>
    <strong>for</strong> j = 1 <strong>to</strong> i <strong>do</strong>
      <strong>let</strong> coef =
        <strong>if</strong> j = 1 || j = i <strong>then</strong> 1 
        <strong>else</strong> get (i - 1, j) + get (i - 1, j - 1) <strong>in</strong>
      label ~text:(string_of_int coef) ~packing ();
      Hashtbl.add mem (i, j) coef
    <strong>done</strong>
  <strong>done</strong></pre>
<p>Nous pouvons maintenant terminer notre application en lui ajoutant un bouton de fermeture. C&rsquo;est le moment de se rappeler que le conteneur <code class="codecolorer text default"><span class="text">GPack.button_box</span></code> est destiné tout spécialement à recevoir des boutons :</p>
<pre><strong>let</strong> quit = 
  <strong>let</strong> bbox = GPack.button_box `HORIZONTAL 
    ~layout:`END 
    ~packing:(vbox#pack ~expand:<strong>false</strong>) () in
  <strong>let</strong> quit = GButton.button ~stock:`QUIT ~packing:bbox#add () <strong>in</strong>
  quit#connect#clicked window#destroy;
  quit</pre>
<p>Pour finir, quelques lignes qui vérifient que le nombre de lignes à afficher est bien passé en argument, et qu&rsquo;il s&rsquo;agit d&rsquo;un entier valide compris entre 1 et 25 (limites arbitraires) :</p>
<pre><strong>let</strong> _ =
  <strong>try</strong>
    <strong>if</strong> Array.length Sys.argv = 2 <strong>then</strong> (
      <strong>let</strong> n = int_of_string Sys.argv.(1) <strong>in</strong>
      <strong>if</strong> n > 0 &#038;&#038; n < 26 <strong>then</strong> create n <strong>else</strong> raise Exit;
      window#show ();
      GMain.main ()
    ) <strong>else</strong> raise Exit 
  <strong>with</strong> _ -> prerr_endline "Usage: triangle.ml TAILLE"</pre>
<p>C&rsquo;est tout ! Il ne nous reste plus qu&rsquo;à lancer l&rsquo;application :<br />
<center><code class="codecolorer text default"><span class="text">ocaml -w s -I +lablgtk2 lablgtk.cma code.ml</span></code></center></p>
<p>Voici aussi un lien vers le <a href="http://ocaml.pastebin.com/f7517af71" target="_blank">code complet de cet atelier</a>.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
