<?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 &#187; Informatique</title>
	<atom:link href="https://blog.developpez.com/ocamlblog/pcategory/informatique/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>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>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>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>
		<item>
		<title>Le Compte est bon</title>
		<link>https://blog.developpez.com/ocamlblog/p8501/informatique/le_compte_est_bon</link>
		<comments>https://blog.developpez.com/ocamlblog/p8501/informatique/le_compte_est_bon#comments</comments>
		<pubDate>Sun, 10 Jan 2010 15:55:08 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Rien de tel que de commencer la nouvelle année par un jeu. Nous allons donc essayer d&#8217;implémenter en OCaml une version proche du jeu Le Compte est bon imaginé par Armand Jammot (dans l&#8217;émission Des chiffres et des lettres que regarde votre grand-mère). Pour ce que ce soit plus pratique, nous allons changer un peu les règles du jeu : Le but du jeu est de calculer un nombre N compris entre 100 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></center></p>
<p>  Rien de tel que de commencer la nouvelle année par un jeu. Nous allons donc essayer d&rsquo;implémenter en OCaml une version proche du jeu <em>Le Compte est bon</em> imaginé par Armand Jammot (dans l&rsquo;émission <a href="http://fr.wikipedia.org/wiki/Des_chiffres_et_des_lettres" target="_blank"><em>Des chiffres et des lettres</em></a> que regarde votre grand-mère). </p>
<p><span id="more-6"></span></p>
<p>Pour ce que ce soit plus pratique, nous allons changer un peu les règles du jeu :</p>
<ul>
<li>Le but du jeu est de calculer un nombre N compris entre 100 et 1000.</li>
<li>On dispose pour cela de 6 entiers choisis dans l&rsquo;intervalle [1; 100].</li>
<li>Le choix des nombres est aléatoire et il n&rsquo;y a pas de doublons.</li>
<li>Les entiers sont combinés entre eux à l&rsquo;aide des opérations +, -, * et /.</li>
</ul>
<p>  Contrairement aux candidats du jeu télévisé, l&rsquo;ordinateur devra trouver toutes les solutions. Nous souhaitons obtenir cette réponse en <strong>une seconde au plus</strong> sur un ordinateur récent (en natif, bien entendu).</p>
<h4>Mais au fait&#8230;</h4>
<p>  Avant de commencer, je ne peux m&rsquo;empêcher de vous proposer une question préliminaire pour évaluer la faisabilité de ce programme : <strong>combien de cas l&rsquo;ordinateur doit-il analyser ?</strong> Je ne donne pas la réponse tout de suite, mais voici tout de même un indice : pour n = 6, cas qui nous intéresse ici, il faut examiner plus de 700 000 expressions.</p>
<h3>Définition d&rsquo;une expression</h3>
<p>  Pour pouvoir travailler efficacement, nous devons d&rsquo;abord définir un type capable de représenter une expression mathématique de la forme <code class="codecolorer text default"><span class="text">(((a + b) - c) * d) / e</span></code>. Il s&rsquo;agit tout simplement d&rsquo;une sorte d&rsquo;arbre binaire dont les feuilles sont des entiers :</p>
<pre><strong>type</strong> expr =
  | Num <strong>of</strong> int              <em>(* Entier.      *)</em>
  | Add <strong>of</strong> expr * expr      <em>(* Somme.       *)</em>
  | Rem <strong>of</strong> expr * expr      <em>(* Différence.  *)</em>
  | Mul <strong>of</strong> expr * expr      <em>(* Produit.     *)</em>
  | Div <strong>of</strong> expr * expr      <em>(* Quotient.    *)</em>
</pre>
<p>Par commodité, et parce que les constructeurs d&rsquo;OCaml ne sont pas des fonctions (il y a, je crois, des extensions camlp4 pour obtenir quelque chose dans ce genre), nous allons nous  simplifier la vie en définissant dès maintenant les quatre fonctions auxiliaires suivantes :</p>
<pre><strong>let</strong> add x y = Add (x, y)
<strong>let</strong> rem x y = Rem (x, y)
<strong>let</strong> mul x y = Mul (x, y)
<strong>let</strong> div x y = Div (x, y)
</pre>
<p>  Pour finir, il nous faut une fonction capable de convertir une expression en chaîne de caractères en vue de son affichage. La méthode retenue ici ajoute quelques parenthèses superflues, mais elle a le double mérite d&rsquo;être simple.</p>
<pre><strong>let</strong> to_string =
  <strong>let</strong> <strong>rec</strong> loop lbr rbr = <strong>function</strong>
    | Num x -> string_of_int x
    | Add (x, y) -> sprintf "%s%s + %s%s" lbr (inner x) (inner y) rbr
    | Rem (x, y) -> sprintf "%s%s - %s%s" lbr (inner x) (inner y) rbr
    | Mul (x, y) -> sprintf "%s%s * %s%s" lbr (inner x) (inner y) rbr
    | Div (x, y) -> sprintf "%s%s / %s%s" lbr (inner x) (inner y) rbr
  <strong>and</strong> inner expr = loop "(" ")" expr <strong>in</strong> loop "" ""</pre>
<h3>Quelques ensembles utiles</h3>
<p>  Intéressons-nous maintenant aux données que nous allons manipuler. L&rsquo;énoncé précise que nous disposons de six entiers uniques choisis au hasard. Dans le cas qui nous intéresse, les <strong>ensembles</strong> (module <code class="codecolorer text default"><span class="text">Set</span></code>) sont particulièrement bien adaptés. Définissons donc :</p>
<pre><strong>module</strong> NSet = Set.Make (
  <strong>struct</strong>
    <strong>type</strong> t = int
    <strong>let</strong> compare = compare
  <strong>end</strong>
)</pre>
<p>  Il nous faut aussi de quoi stocker l&rsquo;ensemble des solutions. Comme nous allons seulement les afficher, nous pouvons définir un <strong>ensemble de chaînes</strong> (mais on peut aussi envisager de stocker un ensemble d&rsquo;expressions en vue de traitements ultérieurs) :</p>
<pre><strong>module</strong> SSet = Set.Make(String)</pre>
<h3>Fonctions auxiliaires</h3>
<p>  Écrivons maintenant une fonction qui renvoie un ensemble constitué de six entiers choisis au hasard dans l&rsquo;intervalle [1; 100]. L&rsquo;initialisation du <a href="http://fr.wikipedia.org/wiki/G%C3%A9n%C3%A9rateur_de_nombres_pseudo-al%C3%A9atoires" target="_blank">générateur de nombres pseudo-aléatoires</a> est contrôlée par un paramètre facultatif, et désactivée par défaut :</p>
<pre><strong>let</strong> get_numbers ?(init = <strong>false</strong>) () =
  <strong>if</strong> init <strong>then</strong> Random.self_init ();
  <strong>let</strong> <strong>rec</strong> loop set = <strong>function</strong>
    | 0 -> set
    | i -> <strong>let</strong> n = Random.int 100 + 1 <strong>in</strong>
      <strong>if</strong> NSet.mem n set <strong>then</strong> loop set i
      <strong>else</strong> loop (NSet.add n set) (i - 1)
  <strong>in</strong> loop NSet.empty 6</pre>
<h3>L&rsquo;algorithme</h3>
<p>  Définissons d&rsquo;abord deux listes auxiliaires qui vont nous être fort utile par la suite :</p>
<pre><strong>let</strong> functions = [add; rem; mul; div]
<strong>let</strong> operators = [( + ); ( - ); ( * ); ( / )]</pre>
<p>  À partir de là, il faut réfléchir un peu. L&rsquo;algorithme d&rsquo;exploration exhaustive fonctionne de la manière suivante (je donne le noms des variables utilisées en gras dans le texte pour faciliter la compréhension du code donné ultérieurement) :</p>
<ul>
<li>Choisir un entier (<strong>y</strong>) et poursuivre l&rsquo;exploration avec les 4 opérations de base (<strong>opr</strong>), en retirant cet entier de l&rsquo;ensemble des entiers  disponibles (<strong>num</strong>).</li>
<li>À chaque niveau, comparer le résultat intermédiaire <strong>x</strong> avec le nombre recherché <strong>res</strong>. En effet, une expression valide (<strong>acc</strong>) ne contient pas nécessairement les 6 entiers&#8230; elle peut être plus simple !</li>
</ul>
<p>  Il existe plusieurs manières d&rsquo;écrire ceci en OCaml. Je vous propose une version qui utilise des fonctions d&rsquo;ordre supérieur de type <code class="codecolorer text default"><span class="text">fold</span></code>, et qui traite séparément le cas où l&rsquo;ensemble d&rsquo;entiers est vide. Il s&rsquo;agit d&rsquo;une optimisation qui fait passer le temps d&rsquo;exécution de 0,2 s à 0,1 s en natif car elle évite de manipuler des <a href="http://fr.wikipedia.org/wiki/Fermeture_%28informatique%29" target="_blank">fermetures</a> inutilement.</p>
<pre><strong>let</strong> <strong>rec</strong> explore res x acc num str =
  <strong>let</strong> str' = <strong>if</strong> res = x <strong>then</strong> SSet.add (to_string acc) str <strong>else</strong> str <strong>in</strong>
  <strong>if</strong> NSet.is_empty num <strong>then</strong> set' <strong>else</strong> (
    List.fold_left2 (<strong>fun</strong> str fct opr ->
      NSet.fold (<strong>fun</strong> y str ->
        explore res (opr x y) (fct acc (Num y)) (NSet.remove y num) str
      ) num str
    ) str' functions operators
  )</pre>
<p>  Il ne nous reste plus qu&rsquo;à écrire une fonction <code class="codecolorer text default"><span class="text">find</span></code> qui reçoit en entrée le nombre à calculer (<strong>res</strong>) et l&rsquo;ensemble des entiers disponibles (<strong>num</strong>). Elle permet d&rsquo;appeler <code class="codecolorer text default"><span class="text">explore</span></code> en lui fournissant des valeurs initiales :</p>
<pre><strong>let</strong> find res num =
  NSet.fold (<strong>fun</strong> x -> 
    explore res x (Num x) (NSet.remove x num)
  ) num SSet.empty</pre>
<h3>Quelques exemples d&rsquo;application</h3>
<p>Contrairement à ce que je pensais avant de coder ce jeu, les solutions sont souvent nombreuses. Par exemple, si vous lancez ce code avec res = 162 et l&rsquo;ensemble {10; 22; 36; 43; 73; 94}, vous verrez qu&rsquo;il existe la bagatelle de <strong>854 solutions</strong> ! Au contraire, certains tirages ne donnent que très peu de solutions. Voici un exemple de sortie avec peu de solutions :</p>
<pre>> Calculer 431 avec les valeurs 34 37 57 58 66 82 
Temps d'exécution : 0.126 s (737280 cas, 4 solutions)
((((37 / 34) + 66) - 58) * 57) - 82
((((37 / 34) - 58) + 66) * 57) - 82
((((66 - 57) * 34) * 82) - 37) / 58
((((66 - 57) * 82) * 34) - 37) / 58</pre>
<h3>Et la devinette ?</h3>
<p>Eh bien, c&rsquo;est simple. Vous disposez de <em>n</em> entiers à combiner avec 4 opérations. Vous pouvez donc construire un total de <strong><em>n</em>! * 4 ^ (<em>n</em> &#8211; 1)</strong> expressions (sans tenir compte de la commutativité et des expressions de moins de 6 chiffres). </p>
<h3>Le mot de la fin</h3>
<p>J&rsquo;ai dit plus haut que ce code s&rsquo;exécute en un dixème de seconde environ sur une machine récente. La méthode exhaustive est donc acceptable&#8230; mais ça n&rsquo;a pas toujours été le cas. Ainsi, lors de la création du jeu, aucune implémentation efficace n&rsquo;était disponible et la recherche d&rsquo;une solution reposait sur le talent des animateurs (au contraire, le jeu intitulé <em>Le mot le plus long</em>, dans cette même émission, possédait déjà une version informatique efficace). Je reste d&rsquo;ailleurs toujours admiratif devant l&rsquo;agilité de certaines personnes à trouver les bonnes combinaisons de nombres et d&rsquo;opérations, quand je peine à venir à bout d&rsquo;une multiplication toute bête <img src="https://blog.developpez.com/ocamlblog/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /></p>
<h3>Pour aller plus loin&#8230;</h3>
<p>Il y a de très nombreuses manières de faire évoluer ce code :</p>
<ul>
<li>Ajouter la notion de commutativité pour éviter les solutions redondantes.</li>
<li>Classer les solutions (courtes vs longues, etc.).</li>
<li>Abstraire le code pour pouvoir utiliser d&rsquo;autres types que <code class="codecolorer text default"><span class="text">int</span></code>.</li>
</ul>
<p>Pour finir, voici le <a href="http://ocaml.pastebin.com/f6519124c" target="_blank">code complet</a> (libre de droits, cela va sans dire) qui a servi de base à la rédaction de ce billet.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GWindow</title>
		<link>https://blog.developpez.com/ocamlblog/p8477/informatique/title_157</link>
		<comments>https://blog.developpez.com/ocamlblog/p8477/informatique/title_157#comments</comments>
		<pubDate>Mon, 28 Dec 2009 07:32: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 ! Dans le précédent billet consacré à LablGTK, nous avons parlé du module GButton. Nous allons maintenant nous pencher sur un autre module incontournable dès que l&#8217;on veut créer une interface graphique : le module GWindow. Le module GWindow définit des fenêtres (GtkWindow), des boîtes de dialogue qui affichent des messages (GtkMessageDialog) ou un contenu plus complexe (GtkDialog). On y trouve aussi des fenêtres spécialisées dans la manipulation des fichiers (GtkFileChooserDialog), des couleurs (GtkColorDialog), [&#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>Dans le précédent billet consacré à LablGTK, nous avons parlé du module GButton. Nous allons maintenant nous pencher sur un autre module incontournable dès que l&rsquo;on veut créer une interface graphique : le module <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk/html/GWindow.html" target="_blank">GWindow</a>.</p>
<p><span id="more-16"></span></p>
<p>Le module <code class="codecolorer text default"><span class="text">GWindow</span></code> définit des fenêtres (<code class="codecolorer text default"><span class="text">GtkWindow</span></code>), des boîtes de dialogue qui affichent des messages (<code class="codecolorer text default"><span class="text">GtkMessageDialog</span></code>) ou un contenu plus complexe (<code class="codecolorer text default"><span class="text">GtkDialog</span></code>). On y trouve aussi des fenêtres spécialisées dans la manipulation des fichiers (<code class="codecolorer text default"><span class="text">GtkFileChooserDialog</span></code>), des couleurs (<code class="codecolorer text default"><span class="text">GtkColorDialog</span></code>), des fontes (<code class="codecolorer text default"><span class="text">GtkFontDialog</span></code>). Enfin, le module <code class="codecolorer text default"><span class="text">GWindow</span></code> fournit une boîte de dialogue &lsquo;À propos&#8230;&rsquo; (<code class="codecolorer text default"><span class="text">GtkAboutDialog</span></code>) qui simplifie le développement des applications.</p>
<h3><a href="http://library.gnome.org/devel/gtk/2.11/GtkWindow.html" target="_blank">GtkWindow</a></h3>
<p>Les fenêtres de type <code class="codecolorer text default"><span class="text">GtkWindow</span></code> sont appelées <em>toplevel</em>. Elles servent de conteneur principal pour une interface et n&rsquo;ont donc pas de widget parent. Voici quelques méthodes  intéressantes pour modifier la taille de la fenêtre :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#fullscreen</span></code></td>
<td>Passer en mode &lsquo;Plein écran&rsquo;</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unfullscreen</span></code></td>
<td>Sortir du mode &lsquo;Plein écran&rsquo;</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#maximize</span></code></td>
<td>Occuper toute la place disponible</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unmaximize</span></code></td>
<td>Réduire à la taille normale</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#stick</span></code></td>
<td>Rendre la fenêtre visible sur tous les bureaux</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unstick</span></code></td>
<td>Rendre la fenêtre visible sur le bureau actif uniquement</td>
</tr>
</table>
<h5>Fenêtre principale et fermeture de l&rsquo;application</h5>
<p>En général, on utilise <code class="codecolorer text default"><span class="text">#event#connect#delete</span></code> pour afficher un message de confirmation de la fermeture à l&rsquo;attention de l&rsquo;utilisateur, et <code class="codecolorer text default"><span class="text">#connect#destroy</span></code> pour appeler la fonction <code class="codecolorer text default"><span class="text">GMain.quit</span></code> et quitter l&rsquo;application.</p>
<h5>Widgets enfants</h5>
<p>Dernier point, et non des moindres : les fenêtres sont des widgets qui ne peuvent contenir qu&rsquo;un seul widget enfant. Par conséquent, toute interface tant soit peu complexe devra faire appel à des boîtes (module <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk/html/GPack.html" target="_blank"><code class="codecolorer text default"><span class="text">GPack</span></code></a>) dont nous parlerons dans le prochain billet.</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkMessageDialog.html" target="_blank">GtkMessageDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkMessageDialog.png" width="390" height="190" alt="" /></center></p>
<p>Les fenêtres de type <code class="codecolorer text default"><span class="text">GtkMessageDialog</span></code> servent à afficher un court message à l&rsquo;attention de l&rsquo;utilisateur. On s&rsquo;en sert notamment pour demander confirmation avant de fermer l&rsquo;application lorsque des modifications ont été apportées depuis le dernier enregistrement. Ces boîtes de dialogue permettent d&rsquo;écrire des textes très lisibles grâce aux <strong>balises Pango</strong> (gras, italique, choix de la taille de la police, etc.).</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkDialog.html" target="_blank">GtkDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkDialog.png" width="606" height="486" alt="" /><br />
Boîte de dialogue de type GtkDialog<br />
</center></p>
<p>Les boîtes de dialogue de type <code class="codecolorer text default"><span class="text">GtkDialog</span></code> sont souvent utilisées pour créer la page &lsquo;Préférences&rsquo; dans une application. Ce sont des fenêtres composées dès l&rsquo;origine d&rsquo;une boîte verticale (méthode <code class="codecolorer text default"><span class="text">#vbox</span></code>) dans laquelle il est possible de placer n&rsquo;importe quelle combinaison de widgets. On ajoute des boutons (fermeture, annulation, validation, aide, etc.) avec les méthodes :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#add_button</span></code></td>
<td>Ajoute un bouton en indiquant le texte qu&rsquo;il contient</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#add_button_stock</span></code></td>
<td>Ajoute un bouton avec une icône associée (<code class="codecolorer text default"><span class="text">GtkStock.id</span></code>)</td>
</tr>
</table>
<p>Les boutons sont insérés dans un widget de type GtkButtonBox accessible avec la méthode #action_area.</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkAboutDialog.png" width="297" height="186" alt="" /></center></p>
<p>La boîte de dialogue <code class="codecolorer text default"><span class="text">GtkAboutDialog</span></code> permet de créer simplement une boîte de dialogue de type &laquo;&nbsp;À propos&#8230;&nbsp;&raquo; pour votre application. Elle permet de décrire brièvement le logiciel. Voici quelques-unes des méthodes disponibles :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_authors</span></code></td>
<td>Nom des développeurs du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_documenters</span></code></td>
<td>Auteurs de la documentation.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_license</span></code></td>
<td>Licence du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_logo</span></code></td>
<td>Logo du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_website</span></code></td>
<td>Site web du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_version</span></code></td>
<td>Version du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_copyright</span></code></td>
<td>Copyright.</td>
</tr>
</table>
<p>Comme vous pouvez le voir, cette boîte de dialogue a été conçue pour uniformiser les applications qui utilisent GTK, mais aussi et surtout pour accélérer le développement d&rsquo;un nouveau logiciel.</p>
<h3>Autres boîtes de dialogue</h3>
<p>Il existe d&rsquo;autres boîtes de dialogue plus spécialisées comme <code class="codecolorer text default"><span class="text">GtkColorSelectionDialog</span></code> (sélection de couleur), <code class="codecolorer text default"><span class="text">GtkFontSelectionDialog</span></code> (sélection de fonte) ou <code class="codecolorer text default"><span class="text">GtkFileChooserDialog</span></code> (sélection de fichier). Plutôt qu&rsquo;un long discours, je vous propose de les découvrir à travers un exemple de code. Il s&rsquo;agit d&rsquo;un éditeur de texte simpliste que vous pouvez tester avec la commande <strong>ocaml -w s -I +lablgtk2 lablgtk.cma test.ml</strong>. De plus, vous verrez que ces quelques boîtes de dialogue ressemblent beaucoup à ce que nous avons vu précédemment avec le module <strong>GButton</strong>.</p>
<p><center><img src="http://blog.developpez.com/media/GWindow Demo.png" width="631" height="326" alt="" /></center></p>
<pre><strong>module</strong> Aux =
  <strong>struct</strong>
    <strong>let</strong> load (text : GText.view) file =
      <strong>let</strong> ich = open_in file <strong>in</strong>
      <strong>let</strong> len = in_channel_length ich <strong>in</strong>
      <strong>let</strong> buf = Buffer.create len <strong>in</strong>
      Buffer.add_channel buf ich len;
      close_in ich;
      text#buffer#set_text (Buffer.contents buf)

    <strong>let</strong> save (text : GText.view) file =
      <strong>let</strong> och = open_out file <strong>in</strong>
      output_string och (text#buffer#get_text ());
      close_out och
  end

<strong>let</strong> _ = GMain.init ()
<em>
(* <a href="http://library.gnome.org/devel/gtk/2.11/GtkWindow.html" target="_blank">GtkWindow</a> - Fenêtre principale. *)</em>
<strong>let</strong> window =
  <strong>let</strong> wnd = GWindow.window 
    ~height:300 
    ~resizable:<strong>false</strong>
    ~position:`CENTER
    ~title:"GWindow Demo" () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkMessageDialog.html" target="_blank">GtkMessageDialog</a> - Court message à l'attention de l'utilisateur. *)</em>
<strong>let</strong> confirm _ =
  <strong>let</strong> dlg = GWindow.message_dialog
    ~message:"&lt;b&gt;&lt;big&gt;Voulez-vous vraiment quitter ?&lt;/big&gt;\n\n\
      Attention :\nToutes les modifications seront perdues.&lt;/b&gt;\n"
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~use_markup:<strong>true</strong>
    ~message_type:`QUESTION
    ~position:`CENTER_ON_PARENT
    ~buttons:GWindow.Buttons.yes_no () <strong>in</strong>
  <strong>let</strong> res = dlg#run () = `NO <strong>in</strong>
  dlg#destroy ();
  res

<strong>let</strong> vbox = GPack.vbox 
  ~spacing:5
  ~border_width:5
  ~packing:window#add ()

<strong>let</strong> text =
  <strong>let</strong> scroll = GBin.scrolled_window
    ~hpolicy:`ALWAYS
    ~vpolicy:`ALWAYS
    ~shadow_type:`ETCHED_IN
    ~packing:vbox#add () <strong>in</strong>
  <strong>let</strong> txt = GText.view ~packing:scroll#add () <strong>in</strong>
  txt#misc#modify_font_by_name "Monospace 10";
  txt

<strong>let</strong> bbox = GPack.button_box `HORIZONTAL
  ~spacing:5
  ~layout:`SPREAD
  ~packing:(vbox#pack ~expand:<strong>false</strong>) ()
<em>
(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkFileChooserDialog.html" target="_blank">GtkFileChooserDialog</a> - Boîte de dialogue d'ouverture et d'enregistrement. *)</em>
<strong>let</strong> action_button stock event action =
  <strong>let</strong> dlg = GWindow.file_chooser_dialog
    ~action:`OPEN
    ~parent:window
    ~position:`CENTER_ON_PARENT
    ~destroy_with_parent:<strong>true</strong> () <strong>in</strong>
  dlg#add_button_stock `CANCEL `CANCEL;
  dlg#add_select_button_stock stock event;
  <strong>let</strong> btn = GButton.button ~stock ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () ->
    <strong>if</strong> dlg#run () = `OPEN <strong>then</strong> Gaux.may action dlg#filename;
    dlg#misc#hide ());
  btn

<strong>let</strong> open_button = action_button `OPEN `OPEN (Aux.load text)
<strong>let</strong> save_button = action_button `SAVE `SAVE (Aux.save text)

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkColorSelectionDialog.html" target="_blank">GtkColorSelectionDialog</a> - Sélection de couleur. *)</em>
<strong>let</strong> color_picker =
  <strong>let</strong> dlg = GWindow.color_selection_dialog
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~position:`CENTER_ON_PARENT () <strong>in</strong>
  dlg#ok_button#connect#clicked (<strong>fun</strong> () -> 
    text#misc#modify_base [`NORMAL, `COLOR dlg#colorsel#color]);
  <strong>let</strong> btn = GButton.button ~label:"Arrière-plan" ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`COLOR_PICKER ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<em>(* <a href="http://library.gnome.org/devel/gtk/stable/GtkFontSelectionDialog.html" target="_blank">GtkFontSelectionDialog</a> - Sélection de fonte. *)</em>
<strong>let</strong> font_button =
  <strong>let</strong> dlg = GWindow.font_selection_dialog
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~position:`CENTER_ON_PARENT () <strong>in</strong>
  dlg#ok_button#connect#clicked (<strong>fun</strong> () -> 
    text#misc#modify_font_by_name dlg#selection#font_name);
  <strong>let</strong> btn = GButton.button ~stock:`SELECT_FONT ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`SELECT_FONT ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a> - Boîte de dialogue "À propos..." *)</em>
<strong>let</strong> about_button =
  <strong>let</strong> dlg = GWindow.about_dialog
    ~authors:["Cacophrene (&lt;cacophrene AT gmail DOT com&gt;)"]
    ~copyright:"Copyright © 2009-2010 Cacophrene"
    ~license:"GNU General Public License v3"
    ~version:"1.0"
    ~website:"http://blog.developpez.com/ocamlblog/"
    ~website_label:"OCamlBlog"
    ~position:`CENTER_ON_PARENT
    ~parent:window
    ~destroy_with_parent:<strong>true</strong> () <strong>in</strong>
  <strong>let</strong> btn = GButton.button ~stock:`ABOUT ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`ABOUT ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<strong>let</strong> _ =
  window#event#connect#delete confirm;
  window#show ();
  GMain.main ()</pre>
<p>Bonne année 2010 et à bientôt pour de nouvelles aventures caméliennes,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Paresse, quand tu nous tiens</title>
		<link>https://blog.developpez.com/ocamlblog/p8447/informatique/title_155</link>
		<comments>https://blog.developpez.com/ocamlblog/p8447/informatique/title_155#comments</comments>
		<pubDate>Thu, 10 Dec 2009 06:47:11 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Ce petit billet s&#8217;intercale insidieusement dans la série de billets consacrés à LablGTK suite à la demande d&#8217;un ami. Il s&#8217;agit de présenter une alternative au mot-clef lazy d&#8217;OCaml. Façon de dire : « alternative » est ici un bien grand mot. Nous allons juste survoler la question et voir qu&#8217;il est possible de simuler facilement un type de données paresseux avec des ingrédients classiques de la programmation fonctionnelle. Principe On veut simuler [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></center></p>
<p>Ce petit billet s&rsquo;intercale insidieusement dans la série de billets consacrés à LablGTK suite à la demande d&rsquo;un ami. Il s&rsquo;agit de présenter une alternative au mot-clef <code class="codecolorer text default"><span class="text">lazy</span></code> d&rsquo;OCaml. Façon de dire : « alternative » est ici un bien grand mot. Nous allons juste <em>survoler la question</em> et voir qu&rsquo;il est possible de simuler facilement un type de données paresseux avec des ingrédients classiques de la programmation fonctionnelle.</p>
<p><span id="more-5"></span></p>
<h3>Principe</h3>
<p>On veut simuler le comportement du module <a href="http://caml.inria.fr/pub/docs/manual-ocaml/libref/Lazy.html" target="_blank"><code class="codecolorer text default"><span class="text">Lazy</span></code></a> d&rsquo;OCaml en n&rsquo;utilisant que des traits classiques d&rsquo;OCaml (c&rsquo;est-à-dire sans faire appel à la magie noire). En particulier, on voudrait écrire une fonction <code class="codecolorer text default"><span class="text">force</span></code> qui se comporte (presque) comme <code class="codecolorer text default"><span class="text">Lazy.force</span></code>. En d&rsquo;autres termes, forcer une suspension qui déclenche l&rsquo;exception <code class="codecolorer text default"><span class="text">X</span></code> la déclenche à chaque nouvel appel de <code class="codecolorer text default"><span class="text">force</span></code>. De même, forcer une suspension déjà évaluée ne nécessite aucun calcul supplémentaire (<a href="http://fr.wikipedia.org/wiki/M%C3%A9moization" target="_blank">mémoïsation</a>). En revanche nous allons laisser tomber le cas des appels cycliques à <code class="codecolorer text default"><span class="text">force</span></code>. </p>
<p>Pour y parvenir, nous allons utiliser :</p>
<ul>
<li>Un type somme pour représenter les états possibles de la suspension.</li>
<li>Des références.</li>
<li>Des fonctions.</li>
</ul>
<h3>Implémentation</h3>
<p>Commençons par définir un type <code class="codecolorer text default"><span class="text">'a state</span></code> qui renseigne sur l&rsquo;<em>état</em> (évalué ou non) d&rsquo;une suspension :</p>
<pre>
<strong>type</strong> 'a state =
  | Maybe <strong>of</strong> (unit -> 'a)
  | Value <strong>of</strong> 'a 
  | Error <strong>of</strong> exn
</pre>
<p>Trois cas de figure sont envisageables : </p>
<ul>
<li>la suspension n&rsquo;a pas été évaluée (<code class="codecolorer text default"><span class="text">Maybe</span></code>),</li>
<li>la suspension a été évaluée et un résultat a été obtenu (<code class="codecolorer text default"><span class="text">Value</span></code>),</li>
<li>l&rsquo;évaluation de la suspension a déclenché une erreur (<code class="codecolorer text default"><span class="text">Error</span></code>).</li>
</ul>
<p>Comme nous souhaitons être en mesure de modifier l&rsquo;état de notre suspension, nous avons besoin d&rsquo;une <em>référence</em>. Nous définissons donc notre type « suspension » de la façon suivante :</p>
<pre><strong>type</strong> 'a lazy_val = 'a state ref</pre>
<p>À partir de là, nous pouvons écrire une fonction qui renvoie une suspension à partir d&rsquo;une fonction <code class="codecolorer text default"><span class="text">f</span></code> et de l&rsquo;argument <code class="codecolorer text default"><span class="text">x</span></code> qu&rsquo;elle reçoit en entrée :</p>
<pre><strong>let</strong> create f = <strong>ref</strong> (Maybe f)</pre>
<p>Nous pouvons maintenant écrire une fonction qui force l&rsquo;évaluation d&rsquo;une suspension. Cette fonction doit :</p>
<ul>
<li>forcer l&rsquo;évaluation si l&rsquo;état est <code class="codecolorer text default"><span class="text">Maybe</span></code>,</li>
<li>renvoyer la valeur précédemment calculée si l&rsquo;état est <code class="codecolorer text default"><span class="text">Value</span></code>,</li>
<li>lever l&rsquo;exception précédemment levée si l&rsquo;état est <code class="codecolorer text default"><span class="text">Error</span></code>.</li>
</ul>
<p>Nous écrivons donc :</p>
<pre><strong>let</strong> <strong>rec</strong> force t =
  <strong>match</strong> !t <strong>with</strong>
  | Value x -> x
  | Error x -> raise x
  | Maybe f -> t := (<strong>try</strong> Value (f ()) <strong>with</strong> x -> Error x);
    force t</pre>
<p>Nous pouvons également écrire une fonction qui renvoie <code class="codecolorer text default"><span class="text">Some x</span></code> si l&rsquo;évaluation de la suspension a donné un résultat <code class="codecolorer text default"><span class="text">x</span></code>, et <code class="codecolorer text default"><span class="text">None</span></code> dans tous les autres cas (évaluation non effectuée ou levée d&rsquo;exception) :</p>
<pre><strong>let</strong> may_get t =
  <strong>match</strong> !t <strong>with</strong>
  | Value x -> Some x
  | _ -> None</pre>
<p>Enfin, nous pouvons écrire une fonction qui indique si l&rsquo;évaluation d&rsquo;une valeur paresseuse a déjà été forcée :</p>
<pre><strong>let</strong> is_value t =
  <strong>match</strong> !t <strong>with</strong>
  | Maybe _ -> <strong>false</strong>
  | _ -> <strong>true</strong></pre>
<h3>Quelques exemples d&rsquo;application</h3>
<h4>Arithmétique</h4>
<p>Nous pouvons commencer par redéfinir les opérations usuelles telles que l&rsquo;addition, la soustraction, la multiplication et la division :</p>
<pre><strong>let</strong> aux f x y = create (<strong>fun</strong> () -> f x y)
<strong>let</strong> ( ++ ) = aux ( + )
<strong>let</strong> ( -- ) = aux ( - )
<strong>let</strong> ( ** ) = aux ( * )
<strong>let</strong> ( // ) = aux ( / )</pre>
<p>Un exemple d&rsquo;application plutôt « tarte à la crème » :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"># let x = 12 // 0;; <br />
val x : int lazy_val = &lt;abstr&gt; <br />
# is_value x;; <br />
- : bool = false <br />
# force x;; <br />
Exception: Division_by_zero.</div></div>
<h4>Les listes paresseuses</h4>
<p>Un ordinateur est incapable de manipuler directement une infinité de valeurs, pour la simple et bonne raison qu&rsquo;il lui faudrait pour cela une mémoire infinie. En revanche, un ordinateur peut tout à fait manipuler des structures <strong>potentiellement</strong> infinies (l&rsquo;adverbe change tout).</p>
<p>Voyons par exemple comment implémenter des listes potentiellement infinies en utilisant notre implémentation des structures paresseuses. D&rsquo;abord, il faut définir un type pour nos listes. Ce sera :</p>
<pre><strong>type</strong> 'a t =
  | Empty 
  | Cell <strong>of</strong> 'a * 'a t lazy_val</pre>
<p>Jusqu&rsquo;ici, rien que de bien classique : une liste est vide (<code class="codecolorer text default"><span class="text">Empty</span></code>) ou constituée d&rsquo;un couple tête/queue (<code class="codecolorer text default"><span class="text">Cell</span></code>). La seule originalité concerne ici la queue, qui est une suspension. </p>
<h5>Initialisation</h5>
<p>Maintenant que le type est fixé, nous pouvons écrire une fonction semblable à <code class="codecolorer text default"><span class="text">Array.init</span></code> :</p>
<pre><strong>let</strong> init f =
  <strong>let</strong> <strong>rec</strong> loop i () =
    Cell (f i, create (loop (i + 1)))
  <strong>in</strong> loop 0 ()
</pre>
<p>Il est alors très facile de créer la liste des entiers naturels :</p>
<pre><strong>let</strong> integers = init (<strong>fun</strong> i -> i)</pre>
<h5>Tête et queue de liste</h5>
<p>Continuons sur notre lancée et écrivons deux fonctions <code class="codecolorer text default"><span class="text">hd</span></code> et <code class="codecolorer text default"><span class="text">tl</span></code> qui renvoient respectivement la <strong>tête</strong> et la <strong>queue</strong> d&rsquo;une liste paresseuse. Comme leur équivalent du module <code class="codecolorer text default"><span class="text">List</span></code>, ces fonctions lèvent l&rsquo;exception <code class="codecolorer text default"><span class="text">Invalid_argument</span></code> lorsqu&rsquo;elles reçoivent une liste vide en entrée :</p>
<pre><strong>let</strong> hd = <strong>function</strong>
  | Empty -> invalid_arg "hd"
  | Cell (x, _) -> x

<strong>let</strong> tl = <strong>function</strong>
  | Empty -> invalid_arg "tl"
  | Cell (_, x) -> force x</pre>
<h5>Conversion en liste standard</h5>
<p>La fonction <code class="codecolorer text default"><span class="text">take</span></code> reçoit un entrée une liste paresseuse <em>t</em> et un entier <em>n</em>. Elle renvoie les <em>n</em> premiers éléments de <em>t</em> stockés dans une liste standard. S&rsquo;il y a moins de <em>n</em> éléments disponibles, tous les éléments restants sont renvoyés.</p>
<pre>
<strong>let</strong> take n t =
  <strong>if</strong> n < 0 <strong>then</strong> invalid_arg "take";
  <strong>let</strong> <strong>rec</strong> loop n = <strong>function</strong>
    | Empty -> []
    | Cell (x, t) -> <strong>if</strong> n = 0 <strong>then</strong> []
      <strong>else</strong> x :: loop (n - 1) (force t)
  <strong>in</strong> loop n t
</pre>
<h5>Filtrage des éléments</h5>
<p>Voici maintenant une fonction de filtrage des éléments d&rsquo;une liste paresseuse. Cette fonction reçoit un entrée un prédicat <em>p</em> et une liste paresseuse <em>t</em>. Elle renvoie la sous-liste constituée des éléments de <em>t</em> qui satisfont le prédicat <em>p</em>.</p>
<pre><strong>let</strong> <strong>rec</strong> filter f = <strong>function</strong>
  | Empty -> Empty
  | Cell (x, t) -> <strong>let</strong> g () = filter f (force t) <strong>in</strong>
    <strong>if</strong> f x <strong>then</strong> Cell (x, create g) <strong>else</strong> g ()
</pre>
<h5>Itérateurs</h5>
<p>La fonction <code class="codecolorer text default"><span class="text">map</span></code> reçoit une fonction <em>f</em> et une liste paresseuse <em>t</em> en entrée et renvoie la liste paresseuse [f(t<sub>0</sub>); f(t<sub>1</sub>); &#8230; f(t<sub>n</sub>)].</p>
<pre>
<strong>let</strong> <strong>rec</strong> map f = <strong>function</strong>
  | Empty -> Empty
  | Cell (x, t) -> <strong>let</strong> g () = map f (force t) <strong>in</strong>
    Cell (f x, create g)
</pre>
<p>On peut également écrire un itérateur <code class="codecolorer text default"><span class="text">map2</span></code> qui manipule deux listes paresseuses supposées de même longueur. Cette fonction est l&rsquo;équivalent de la fonction <a href="http://www.haskell.org/ghc/docs/6.10.4/html/libraries/base/Prelude.html#19" target="_blank"><code class="codecolorer text default"><span class="text">zipWith</span></code></a> d&rsquo;Haskell.</p>
<pre><strong>let</strong> <strong>rec</strong> map2 f x y =
  <strong>match</strong> x, y <strong>with</strong>
  | Empty, Empty -> Empty
  | Cell (x, t1), Cell (y, t2) -> <strong>let</strong> g () = map2 f (force t1) (force t2) <strong>in</strong>
    Cell (f x y, create g)
  | _ -> invalid_arg "map2"
</pre>
<h5>Longueur de liste</h5>
<p>On peut aussi écrire une fonction <code class="codecolorer text default"><span class="text">length</span></code> qui renvoie, non pas la longueur réelle de la liste (cela n&rsquo;a aucun sens puisqu&rsquo;on manipule une liste potentiellement infinie), mais <strong>le nombre de suspensions déjà évaluées</strong>. Cela donne quelque chose comme ceci :</p>
<pre><strong>let</strong> length x =
  <strong>let</strong> <strong>rec</strong> loop n = <strong>function</strong>
    | Empty -> n
    | Cell (_, t) -> <strong>match</strong> may_get t <strong>with</strong>
      | Some t -> loop (n + 1) t
      | _ -> n
  <strong>in</strong> loop 0 x</pre>
<h4>Liste des entiers impairs</h4>
<p>Les <em>entiers naturels impairs</em> possèdent la propriété suivante : leur écriture binaire contient toujours un bit de poids faible égal à 1. On peut donc écrire une fonction <code class="codecolorer text default"><span class="text">odd</span></code> en utilisant l&rsquo;opérateur bit-à-bit <code class="codecolorer text default"><span class="text">land</span></code> (on pouvait aussi se servir du modulo pour ne pas se compliquer la vie&#8230;), puis extraire de la liste des entiers naturels la liste des entiers impairs :</p>
<pre><strong>let</strong> odd x = x <strong>land</strong> 1 = 1 
<strong>let</strong> odd_list = filter odd integers
</pre>
<h4>Liste des puissances de 2</h4>
<p>Dans le même genre, on peut construire la liste des puissances de 2. Il s&rsquo;agit pour le coup d&rsquo;une version <em>très</em> inefficace (dans l&rsquo;interpréteur, mon ordinateur met 26 s pour calculer les 25 premières puissances de 2).</p>
<pre><strong>let</strong> two x = x > 0 &#038;&#038; x <strong>land</strong> (x - 1) = 0
<strong>let</strong> two_list = filter two integers
</pre>
<h4>Liste des nombres de Fibonacci</h4>
<p>L&rsquo;évaluation retardée permet également de construire la liste des nombres de Fibonacci (cf. la notion associée de <a href="http://en.wikipedia.org/wiki/Corecursion" target="_blank">corécursion</a>). L&rsquo;idée est simple : on fournit les deux cas de base (ici 0 et 1, mais certains commencent avec 1 et 1) puis on indique que la construction des éléments suivants s&rsquo;effectue en appliquant la formule F<sub>n + 1</sub> = F<sub>n</sub> + F<sub>n &#8211; 1</sub>. J&rsquo;utilise la bibliothèque <code class="codecolorer text default"><span class="text">Num</span></code> car les termes de la suite de Fibonacci sont rapidement <em>grands</em>.</p>
<pre><strong>open</strong> Num

<strong>let</strong> fib =
  <strong>let</strong> <strong>rec</strong> aux a b =
    Cell (a, create (<strong>fun</strong> () -> aux b (a +/ b)))
  <strong>in</strong> aux (Int 0) (Int 1)</pre>
<p>Exemple : <code class="codecolorer text default"><span class="text">take 10 fib = [Int 0; Int 1; Int 1; Int 2; Int 3; Int 5; Int 8; Int 13; Int 21; Int 34]</span></code>. Voici une autre version moins efficace (dans cet exemple, <code class="codecolorer text default"><span class="text">aux</span></code> pourrait ne pas être une fonction, mais les restrictions imposées par OCaml en matière de récursion du fait de son mode d&rsquo;évaluation strict obligent à procéder de la sorte) :</p>
<pre><strong>let</strong> fib =
  <strong>let</strong> <strong>rec</strong> aux () =
    Cell (Int 0, create (<strong>fun</strong> () -> 
      Cell (Int 1, create (<strong>fun</strong> () ->
        <strong>let</strong> x = aux () <strong>in</strong>
        map2 (+/) x (tl x)
  )))) <strong>in</strong> aux ()</pre>
<h3>Bibliographie</h3>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Lazy_evaluation" target="_blank">Principe de l&rsquo;évaluation retardée</a> (en anglais)</li>
<li><a href="http://blog.interlinked.org/tutorials/haskell_laziness.html" target="_blank">L&rsquo;évaluation retardée avec Haskell</a> (en anglais)</li>
<li><a href="http://www.fullbooks.com/The-first-1001-Fibonacci-Numbers.html" target="_blank">Les 1001 premiers termes de la suite de Fibonacci</a> (attention, initialisation avec 1 et 1).</li>
<li><a href="http://en.wikibooks.org/wiki/Clojure_Programming/Examples/Lazy_Fibonacci" target="_blank">La suite de Fibonacci et évaluation retardée avec Clojure</a> (en anglais)</li>
</ul>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GButton</title>
		<link>https://blog.developpez.com/ocamlblog/p8433/informatique/lablgtk_revue_de_detail</link>
		<comments>https://blog.developpez.com/ocamlblog/p8433/informatique/lablgtk_revue_de_detail#comments</comments>
		<pubDate>Mon, 07 Dec 2009 09:05:17 +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 ! Ce billet est le premier d&#8217;une longue série consacrée à LablGTK et, plus particulièrement, à la présentation détaillée des modules qui la constituent. Beaucoup de questions sur le forum caml de developpez.com plaident en faveur de cette idée. Il est vrai que la bibliothèque comporte plusieurs de 20 modules d&#8217;utilisation courante&#8230; ce qui n&#8217;est pas toujours évident au premier abord. Dans ce billet, nous allons commencer par quelque chose de simple : le [&#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>Ce billet est le premier d&rsquo;une longue série consacrée à LablGTK et, plus particulièrement, à la présentation détaillée des modules qui la constituent. Beaucoup de questions sur le forum caml de developpez.com plaident en faveur de cette idée. Il est vrai que la bibliothèque comporte plusieurs de 20 modules d&rsquo;utilisation courante&#8230; ce qui n&rsquo;est pas toujours évident au premier abord. Dans ce billet, nous allons commencer par quelque chose de simple : le module <strong>GButton</strong>.</p>
<p><span id="more-15"></span></p>
<p>Le module GButton définit des widgets cliquables communément appelés boutons. Ce sont des boutons simples (<strong>GtkButton</strong>), des bouton à bascule (<strong>GtkToggleButton</strong>), des cases à cocher (<strong>GtkCheckButton</strong>) ou des choix multiples (<strong>GtkRadioButton</strong>). On y trouve également des versions spécialisées dans la sélection d&rsquo;une fonte (<strong>GtkFontButton</strong>) ou d&rsquo;une couleur (<strong>GtkColorButton</strong>), ou encore la définition de liens cliquables (<strong>GtkLinkButton</strong>). Enfin, c&rsquo;est avec ce module que l&rsquo;on peut définir des barres d&rsquo;outils (<strong>GtkToolbar</strong>).</p>
<p><center><img src="http://blog.developpez.com/media/GButton_01.png" width="331" height="360" alt="" /></center></p>
<p><strong>Les boutons simples (<a href="http://library.gnome.org/devel/gtk/unstable/GtkButton.html" target="_blank">GtkButton</a>)</strong><br />
Les boutons simples sont principalement utilisés pour répondre à un clic de l&rsquo;utilisateur (<strong>#connect#clicked</strong>). Ils possèdent un seul état et se retrouvent surtout dans les boîtes de dialogue sous la forme de boutons <em>OK</em>, <em>Appliquer</em> ou <em>Annuler</em>. Un bouton simple peut contenir une icône prédéfinie (module <strong>GtkStock</strong>) ou issue de n&rsquo;importe quel widget (<strong>#set_image</strong>).</p>
<p><strong>Les boutons à bascule (<a href="http://library.gnome.org/devel/gtk/unstable/GtkToggleButton.html" target="_blank">GtkToggleButton</a>)</strong><br />
Les boutons à bascule sont des boutons à deux états (état enfoncé et état relâché). Leur état peut être déterminé avec <strong>#active</strong> (<strong>true</strong> quand le bouton est enfoncé). Pour les applications partielles, on utilisera quelque chose comme ceci :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let foo toggle () = if toggle#active then (*...*) else (*...*) <br />
&nbsp;<br />
let toggle = <br />
&nbsp; let toggle = GButton.toggle_button () in <br />
&nbsp; toggle#connect#toggled (foo toggle); <br />
&nbsp; toggle</div></div>
<p><strong>Les cases à cocher (<a href="http://library.gnome.org/devel/gtk/unstable/GtkCheckButton.html" target="_blank">GtkCheckButton</a>)</strong><br />
Les cases à cocher permettent de sélectionner des options qui ne sont pas exclusives. En d&rsquo;autres termes, plusieurs choix peuvent être sélectionnés simultanément. La fonction <strong>GButton.check_button</strong> qui permet de les créer renvoie un widget de type <strong>toggle_button</strong>. En d&rsquo;autres termes, les cases à cocher sont des boutons à bascule dont l&rsquo;apparence a été modifiée. Pour le programmeur, la manipulation est exactement la même que pour les boutons à bascule.</p>
<p><strong>Les choix multiples (<a href="http://library.gnome.org/devel/gtk/unstable/GtkRadioButton.html" target="_blank">GtkRadioButton</a>)</strong><br />
Les choix multiples obligent l&rsquo;utilisateur à faire un choix parmi un ensemble de cas exclusifs. En d&rsquo;autres termes, lorsqu&rsquo;un nouveau choix est effectué, le choix précédent est automatiquement désélectionné. Pour assurer ce comportement, il ne faut pas oublier de définir le paramètre <strong>group</strong> pour relier les différents choix. Voici un exemple :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let days = [&quot;Monday&quot;; &quot;Tuesday&quot;; &quot;Wednesday&quot;; &quot;Thursday&quot;; &quot;Friday&quot;; &quot;Saturday&quot;; &nbsp;<br />
&nbsp; &quot;Sunday&quot;] <br />
&nbsp;<br />
let build packing = <br />
&nbsp; let rec loop group = function <br />
&nbsp; &nbsp; &nbsp; | [] -&gt; [] <br />
&nbsp; &nbsp; &nbsp; | label :: t -&gt; let btn = GButton.radio_button ~label ?group ~packing () in <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; btn :: loop (Some (Gaux.default btn#group ~opt:group)) t <br />
&nbsp; in loop None days</div></div>
<p><strong>Sélection de fonte (<a href="http://library.gnome.org/devel/gtk/unstable/GtkFontButton.html" target="_blank">GtkFontButton</a>)</strong><br />
Ce bouton permet de choisir une police de caractères. L&rsquo;événement associé est <strong>#connect#font_set</strong>, qui est déclenché chaque fois qu&rsquo;une fonte est sélectionnée par l&rsquo;utilisateur. La fonte peut être récupérée grâce à <strong>#font_name</strong>. Il s&rsquo;agit d&rsquo;une chaîne de caractères que l&rsquo;on peut convertir dans un type plus adapté (<strong>Pango.font_description</strong>) avec la fonction <strong>Pango.Font.from_string</strong>. On a alors accès à de nombreuses infos telles que la taille, la famille, l&rsquo;épaisseur&#8230;</p>
<p><center><img src="http://blog.developpez.com/media/font-selection.png" width="458" height="362" alt="" /></center></p>
<p><strong>Sélection de couleur (<a href="http://library.gnome.org/devel/gtk/unstable/GtkColorButton.html" target="_blank">GtkColorButton</a>)</strong><br />
Ce bouton permet de choisir une couleur. L&rsquo;événement associé est <strong>#connect#color_set</strong>. La couleur est obtenue avec <strong>#color</strong> et renvoyée au format <strong>Gdk.color</strong>. On récupère les composantes rouge, verte et bleue d&rsquo;une couleur au format Gdk.color avec les fonctions <strong>Gdk.Color.red</strong>, <strong>Gdk.Color.green</strong> et <strong>Gdk.Color.blue</strong>. Attention : les composantes sont sur 16 bits. Ne cherchez pas à reprogrammer ce bouton vous-mêmes, car ce serait long et inutile. Le bouton de sélection de couleur est vraiment bien conçu !</p>
<p><center><img src="http://blog.developpez.com/media/color-picker.png" width="588" height="307" alt="" /></center></p>
<p><strong>Lien cliquable (<a href="http://library.gnome.org/devel/gtk/unstable/GtkLinkButton.html" target="_blank">GtkLinkButton</a>)</strong><br />
Ce bouton permet de définir un lien (par exemple vers une page web) auquel on peut accéder par simple clic. On le retrouve notamment dans la boîte de dialogue &laquo;&nbsp;À propos&#8230;&nbsp;&raquo; (<a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a>). Son utilisation est très simple :</p>
<p><code class="codecolorer text default"><span class="text">let link = GButton.link_button &quot;http://www.google.fr/&quot; ~label:&quot;Google France&quot; ()</span></code></p>
<p><strong>Barres d&rsquo;outils (<a href="http://library.gnome.org/devel/gtk/unstable/GtkToolbar.html" target="_blank">GtkToolbar</a>)</strong><br />
Les barres d&rsquo;outils sont utilisées pour grouper plusieurs boutons sur une petite zone de l&rsquo;interface. Elles ont une orientation (verticale ou horizontale) et un style (icône seule (<strong>`ICONS</strong>), texte seul (<strong>`TEXT</strong>), icône et texte en dessous (<strong>`BOTH</strong>) ou icône et texte à côté (<strong>`BOTH_HORIZ</strong>)).</p>
<p>Les éléments d&rsquo;une barre d&rsquo;outils sont des widgets appelés <em>toolitems</em>. On retrouve la plupart des catégories de boutons :</p>
<ul>
<li>Des séparateurs : <a href="http://library.gnome.org/devel/gtk/unstable/GtkSeparatorToolItem.html" target="_blank">GtkSeparatorToolItem</a></li>
<li>Des boutons simples : <a href="http://library.gnome.org/devel/gtk/unstable/GtkToolButton.html" target="_blank">GtkToolButton</a></li>
<li>Des boutons à bascule : <a href="http://library.gnome.org/devel/gtk/unstable/GtkToggleToolButton.html" target="_blank">GtkToggleToolButton</a></li>
<li>Des choix multiples : <a href="http://library.gnome.org/devel/gtk/unstable/GtkRadioToolButton.html" target="_blank">GtkRadioToolButton</a></li>
<li>Des boutons avec menu déroulant : <a href="http://library.gnome.org/devel/gtk/unstable/GtkMenuToolButton.html" target="_blank">GtkMenuToolButton</a></li>
</ul>
<p><center><img src="http://blog.developpez.com/media/GtkToolbar.png" width="672" height="522" alt="" /></center><br />
Voici le code qui a permis d&rsquo;obtenir l&rsquo;image ci-dessus :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let window = <br />
&nbsp; GMain.init (); <br />
&nbsp; let wnd = GWindow.window <br />
&nbsp; &nbsp; &nbsp; ~title:&quot;GtkToolbar demo&quot; <br />
&nbsp; &nbsp; &nbsp; ~position:`CENTER <br />
&nbsp; &nbsp; &nbsp; ~resizable:false <br />
&nbsp; &nbsp; &nbsp; ~width:800 ~height:600 () in <br />
&nbsp; wnd#connect#destroy GMain.quit; <br />
&nbsp; wnd <br />
&nbsp;<br />
let box = GPack.vbox ~packing:window#add () <br />
&nbsp;<br />
let toolbar = GButton.toolbar &nbsp;<br />
&nbsp; ~orientation:`HORIZONTAL &nbsp;<br />
&nbsp; ~style:`ICONS <br />
&nbsp; ~packing:(box#pack ~expand:false) () <br />
&nbsp;<br />
let days = &nbsp;<br />
&nbsp; let menu = GMenu.menu () in <br />
&nbsp; List.iter (fun label -&gt; ignore (GMenu.menu_item ~label ~packing:menu#add ())) <br />
&nbsp; &nbsp; &nbsp; [&quot;Lundi&quot;; &quot;Mardi&quot;; &quot;Mercredi&quot;; &quot;Jeudi&quot;; &quot;Vendredi&quot;; &quot;Samedi&quot;; &quot;Dimanche&quot;]; <br />
&nbsp; menu <br />
&nbsp;<br />
let data = [`B `NEW; `B `OPEN; `B `SAVE; `S; `B `CUT; `B `COPY; `B `PASTE; `S; <br />
&nbsp;`T &quot;Bascule&quot;; `S; `M days ] <br />
&nbsp;<br />
let _ = <br />
&nbsp; let packing = toolbar#insert in <br />
&nbsp; List.iter (function <br />
&nbsp; &nbsp; &nbsp; | `S -&gt; ignore (GButton.separator_tool_item ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `B stock -&gt; ignore (GButton.tool_button ~stock ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `T label -&gt; ignore (GButton.toggle_tool_button ~label ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `M menu -&gt; ignore (GButton.menu_tool_button ~label:&quot;Foo&quot; ~menu ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | _ -&gt; () <br />
&nbsp; ) data <br />
&nbsp;<br />
let _ = <br />
&nbsp; window#show (); <br />
&nbsp; GMain.main ()</div></div>
<p>Mais ce n&rsquo;est pas tout ! En fait, on peut insérer à peu près n&rsquo;importe quel widget dans une barre d&rsquo;outils à condition de l&rsquo;empaqueter au préalable dans l&rsquo;unité de base des barres d&rsquo;outils : le <a href="http://library.gnome.org/devel/gtk/unstable/GtkToolItem.html" target="_blank">GtkToolItem</a>.</p>
<p><center><img src="http://blog.developpez.com/media/GtkToolbar-2.png" width="672" height="105" alt="" /></center><br />
Voici les quelques lignes qui ont permis d&rsquo;insérer une fenêtre avec barres de défilement (<a href="http://library.gnome.org/devel/gtk/unstable/GtkScrolledWindow.html" target="_blank">GtkScrolledWindow</a>) dans une barre d&rsquo;outils :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let toolbar = GButton.toolbar &nbsp;<br />
&nbsp; ~orientation:`HORIZONTAL &nbsp;<br />
&nbsp; ~style:`BOTH () <br />
&nbsp;<br />
let item = GButton.tool_item ~packing:toolbar#insert () <br />
&nbsp;<br />
let scroll = GBin.scrolled_window &nbsp;<br />
&nbsp; ~hpolicy:`NEVER &nbsp;<br />
&nbsp; ~vpolicy:`ALWAYS &nbsp;<br />
&nbsp; ~width:160 ~height:20 <br />
&nbsp; ~packing:item#add ()</div></div>
<p>C&rsquo;est tout pour aujourd&rsquo;hui. Dans un prochain billet, nous aborderons un module de grande importance : <strong>GWindow</strong>.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
