<?xml version="1.0" encoding="ISO-8859-1"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title><![CDATA[Forum du club des développeurs et IT Pro - Blogs - Func' programming - un blog homéopathique par stendhal666]]></title>
		<link>https://www.developpez.net/forums/blogs/643915-stendhal666/</link>
		<description>Developpez.com, le Club des Développeurs et IT Pro</description>
		<language>fr</language>
		<lastBuildDate>Sat, 25 Apr 2026 08:18:58 GMT</lastBuildDate>
		<generator>vBulletin</generator>
		<ttl>15</ttl>
		<image>
			<url>https://forum.developpez.be/images/misc/rss.jpg</url>
			<title><![CDATA[Forum du club des développeurs et IT Pro - Blogs - Func' programming - un blog homéopathique par stendhal666]]></title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/</link>
		</image>
		<item>
			<title>SFINAE Interlude - C++ avancé</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b1008/sfinae-interlude-cpp-avance/</link>
			<pubDate>Wed, 06 Jan 2016 15:03:29 GMT</pubDate>
			<description>Dans le billet précédent...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>Dans<a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b1003/detection-associations-frequentes-cpp-troisieme-partie-interface-digne-nom/" target="_blank"> le billet précédent</a>, nous avons utilisé une assertion statique. Ces assertions sont faites lors de la compilation&nbsp;: si elles échouent, le programme ne compile pas et –&nbsp;et c'est là l'intérêt principal&nbsp;– le compilateur affiche un message clair, que vous avez défini, plutôt qu'une longue suite d'erreurs template illisibles. La STL C++11 définit un certain nombre de conditions qui peuvent être utilisées dans une expression static_assert. std::is_same&lt;T, U&gt; est l'exemple que nous avons utilisé pour vérifier que l'itérateur fourni à la fonction scanpattern était bien un std::random_access_iterator. Il arrive un point, cependant, où l'on doit définir soi-même la condition de l'assertion statique. </i><br />
<br />
<b>Is it a good processor&nbsp;?</b><br />
Prenons une fonction <i>mineTree</i>, par exemple, qui prend pour argument un <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">Processor</span> qui doit posséder un opérateur de fonction applicable à un <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::pair&lt;std::vector&lt;Item&gt;, int&gt;</span> (c'est la forme des<i> frequent patterns</i>). Il n'existe pas de condition standard qui permette de vérifier cela et il va falloir créer la nôtre.<br />
<br />
La signature de notre condition sera&nbsp;: <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">template &lt;class Type, class Arg&gt; has_func_operator</span>. <br />
Nous pourrons l'utiliser de la façon suivante&nbsp;: <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">static_assert</span><span style="color: black;">&#40;</span>has_func_operator&lt;Processor, std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;::value,
                   <span style="color: #FF0000;">&quot;Bad Processor Error: Processor must implement func operator with signature operator()(Arg)&quot;</span><span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div>au début de la fonction <i>mineTree</i>.<br />
<br />
<b>Qu'est-ce qu'une condition statique&nbsp;?</b><br />
<span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">static_assert</span> exige une condition statique, c'est-à-dire une condition qu'il est possible de vérifier à la compilation. Il est donc impossible d'écrire, par exemple&nbsp;:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">char</span> c;
std::cin &gt;&gt; c;
<span style="color: #0000ff;">static_assert</span><span style="color: black;">&#40;</span>c == <span style="color: #FF0000;">'a'</span>, <span style="color: #FF0000;">&quot;erreur: c != a&quot;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// c'est d&eacute;termin&eacute; &agrave; l'ex&eacute;cution !</span></pre></td></tr></table></pre>
</div><br />
Donc toute la difficulté de l'exercice est d'obtenir l'information sans entrer dans un contexte d'exécution, appelé aussi contexte d'évaluation. Prenons l'exemple de la condition <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::is_same</span>, comment peut-elle être implémentée&nbsp;? Assez simplement, en fait, même avec les versions plus anciennes du standard&nbsp;: on utilise la possibilité de spécialisation partielle des templates, processus qui se déroule entièrement à la compilation&nbsp;:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:132px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T, <span style="color: #0000ff;">class</span> U&gt;
<span style="color: #0000ff;">struct</span> is_same <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">bool</span> value = <span style="color: #0080ff;">false</span>; <span style="color: #808080;">// T et U sont des types diff&eacute;rents</span>
<span style="color: black;">&#125;</span>;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt;
<span style="color: #0000ff;">struct</span> is_same&lt;T, T&gt; <span style="color: black;">&#123;</span> <span style="color: #808080;">//, mais l&agrave; T et U sont le m&ecirc;me type!</span>
  <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">bool</span> value = <span style="color: #0080ff;">true</span>; <span style="color: #808080;">// donc is_same::value = true</span>
<span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
<b>Hélas, tout n'est pas si simple</b><br />
Certaines conditions sont plus difficiles à vérifier que d'autres. Celle que nous recherchons, <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">has_func_operator</span>, ne peut pas être implémentée uniquement avec les spécialisations partielles. On peut de cette façon vérifier le type d'une fonction, mais pas son existence&nbsp;: pour que la spécialisation fonctionne, il faut qu'au moins une des spécialisations soit valide. Il faut trouver une façon d'utiliser le contexte de compilation d'une façon que l'erreur devienne constructive –&nbsp;et c'est exactement le rôle de cette technique nommée SFINAE.<br />
<br />
<b>Substitution failure is not an error</b><br />
L'échec d'une substitution n'est pas une erreur. Décortiquons cela&nbsp;:<br />
<i><u>l'échec d'une substitution</u></i>&nbsp;: pour instancier une fonction template surchargée (avec plusieurs signatures), le compilateur regarde les différentes signatures possibles et choisit celle qui est la plus adaptée. C'est le principe de la substitution&nbsp;: on substitue à une signature générique une signature déterminée.<br />
<i><u>n'est pas une erreur</u></i>&nbsp;: vous me direz que c'est la même chose pour une fonction normale, sans template&nbsp;: certes, mais avec une différence importante&nbsp;: si une des fonctions normales qui peut être choisie est mal formée, le compilateur refusera de compiler. Ce n'est pas le cas lorsqu'il s'agit d'une fonction template. Pourquoi&nbsp;? Parce qu'une fonction template qui n'est pas appelée n'est pas instanciée. Pour le compilateur, elle n'existe pas. Donc si elle est mal formée, peu importe -&gt; l'échec d'une substitution n'est pas une erreur.<br />
<br />
<b>Concrètement, comment ça marche&nbsp;?</b><br />
Comme une fonction template qui n'est pas retenue lors de l'étape de substitution n'est pas instanciée, deux fonctions de même nom peuvent être surchargées aussi bien du côté des arguments que du côté du type de retour. En examinant le type de retour, on peut donc savoir quelle surcharge a été appelée. C'est ainsi qu'on utilisait SFINAE dans les versions du standard antérieures à C++11. Par exemple, voici <a href="http://en.cppreference.com/w/cpp/language/sfinae" target="_blank">une astuce</a> pour déterminer si un type est une classe. Elle repose sur le fait qu'une signature comportant un pointeur sur un membre non statique d'un type provoquera un échec de substitution si le type n'est pas une classe&nbsp;:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:84px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">char</span> is_a_class<span style="color: black;">&#91;</span><span style="color: #cc66cc;">2</span><span style="color: black;">&#93;</span>; <span style="color: #808080;">// on diff&eacute;rencie les types de retour par leur taille</span>
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">char</span> is_not_a_class; <span style="color: #808080;">// on est au moins s&ucirc;r que sizeof(char) == 1</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt; is_a_class func<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span> T::*<span style="color: black;">&#41;</span>; <span style="color: #808080;">// pointeur sur un membre int - cette signature sera utilis&eacute;e si T est une classe, sinon elle &eacute;chouera...</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt; is_not_a_class func<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span>;    <span style="color: #808080;">// ...et c'est cette signature qui sera utilis&eacute;e.</span></pre></td></tr></table></pre>
</div><br />
<b>La moitié du chemin</b><br />
Nous avons fait la moitié du chemin, reste la deuxième. Comme vous pouvez le constater, les signatures de <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">func</span> ci-dessus ne sont pas définies. Ce n'est pas gênant, car nous devons rester en dehors du contexte d'évaluation ou d'instanciation. Avant C++11, le moyen de rester dans ce contexte était offert par l'opérateur <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">sizeof</span>.  C'est la raison pour laquelle j'ai pris deux types de retours dont on peut être certain qu'ils sont de tailles différentes. Nous allons pouvoir encapsuler notre résolution de substitution et résoudre la question de la surcharge retenue&nbsp;:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:156px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt;
<span style="color: #0000ff;">struct</span> is_class <span style="color: black;">&#123;</span>
<span style="color: #808080;">// comme avant</span>
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">char</span> is_a_class<span style="color: black;">&#91;</span><span style="color: #cc66cc;">2</span><span style="color: black;">&#93;</span>;
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">char</span> is_not_a_class; 
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt; is_a_class func<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span> T::*<span style="color: black;">&#41;</span>; 
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T&gt; is_not_a_class func<span style="color: black;">&#40;</span>T<span style="color: black;">&#41;</span>;
<span style="color: #808080;">// et on rajoute</span>
<span style="color: #0000ff;">static</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> value = <span style="color: #0000ff;">sizeof</span><span style="color: black;">&#40;</span>func&lt;T&gt;<span style="color: black;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> == <span style="color: #0000ff;">sizeof</span><span style="color: black;">&#40;</span>is_a_class<span style="color: black;">&#41;</span>; <span style="color: #808080;">// static const: on reste dans le contexte de compilation tant qu'on ne prend pas l'adresse de value (l&eacute;g&egrave;re simplification)</span>
<span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
<b>De retour au processeur et à C++11</b><br />
L'implémentation de SFINAE qu'on a vue est très astucieuse, mais c'est de l'histoire ancienne. C++11 offre des ressources plus puissantes pour la métaprogrammation. C'est avec ces ressources nouvelles que nous résoudrons la question initiale, l'écriture de <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">has_func_operator</span>. En voici le code, l'explication vient&nbsp;:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
<span style="color: #808080;">// 1</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T, <span style="color: #0000ff;">class</span> Arg&gt;
<span style="color: #0000ff;">auto</span> <span style="color: #0000ff;">constexpr</span> has_func_operator_intern<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: black;">&#41;</span> -&gt; <span style="color: #0000ff;">decltype</span><span style="color: black;">&#40;</span>std::declval&lt;T&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>Arg<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, <span style="color: #0000ff;">bool</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">return</span> <span style="color: #0080ff;">true</span>;
<span style="color: black;">&#125;</span>
<span style="color: #808080;">// 2</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> , <span style="color: #0000ff;">class</span>&gt;
<span style="color: #0000ff;">bool</span> <span style="color: #0000ff;">constexpr</span> has_func_operator_intern<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">return</span> <span style="color: #0080ff;">false</span>;
<span style="color: black;">&#125;</span>
<span style="color: #808080;">// 3</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> T, <span style="color: #0000ff;">class</span> Arg&gt;
<span style="color: #0000ff;">struct</span> has_func_operator <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">bool</span> value = has_func_operator_intern&lt;T, Arg&gt;<span style="color: black;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span>;
<span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
Nous commençons par la deuxième fonction&nbsp;:<br />
<ul><li style="">c'est une fonction <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">constexpr</span>&nbsp;: c'est-à-dire que, sous réserve que son contenu le permette, elle peut-être appelée à la compilation, donc en dehors d'un contexte d'exécution&nbsp;;</li><li style="">elle a pour signature l'ellipse <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">(...)</span>&nbsp;: au moment de la substitution, c'est la signature qui a la priorité la plus faible&nbsp;; elle ne sera utilisée que si toutes les autres substitutions ont échoué.</li></ul><br />
<br />
La première fonction est plus compliquée&nbsp;:<br />
<ul><li style="">son type de retour est indiqué après la flèche <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">-&gt;</span> . C'est une nouvelle syntaxe de C++11: le mot-clé <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">auto</span> est utilisé à la place du type de retour et précisé après la flèche&nbsp;;</li><li style="">son type de retour est le résultat de l'expression <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">decltype</span>&nbsp;; comme <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">sizeof</span>, <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">decltype</span> reste dans le contexte de compilation. Elle retourne le type de l'expression donnée en argument&nbsp;;</li><li style="">l'argument de <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">decltype</span> est composé autour de l'opérateur virgule&nbsp;: ses deux opérandes sont évalués, mais c'est celui de droite qui est renvoyé&nbsp;;</li><li style="">l'opérande de droite initialise un <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">bool</span>&nbsp;;  <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">decltype</span> retournera donc le type <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">bool</span> de même que la fonction has_func_operator_intern&nbsp;;</li><li style="">l'opérande de gauche est complexe. Plus simplement on aurait pu l'écrire <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">T()(Arg())</span>, mais cela aurait posé une difficulté: si T n'a pas de constructeur accessible, la substitution échouera. Si T est une lambda, qui pourrait pourtant avoir un opérateur de fonction comme on le recherche, la substitution échouerait. C'est le rôle de <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::declval</span> de résoudre ce problème.</li><li style=""><span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::declval&lt;T&gt;()</span> retourne une référence sur l'objet T, ce qui permet de l'utiliser –&nbsp;en dehors du contexte d'exécution évidemment, uniquement dans celui de la déduction des types&nbsp;–pour appeler une fonction membre d'une classe sans avoir à invoquer son constructeur. Donc nous avons une référence sur un objet T inexistant qui nous permet d'appeler son opérateur de fonction.</li></ul><br />
<br />
La troisième fonction est toute simple&nbsp;: c'est seulement une enveloppe autour des deux premières qui évite d'utiliser directement SFINAE en écrivant&nbsp;:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:36px;">has_func_operator_intern&lt;T, Arg&gt;<span style="color: black;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span>;</pre>
</div>dans le corps du programme. De plus, elle harmonise l'interface de notre condition statique avec l'interface des conditions proposées par la STL.<br />
<br />
<b>En conclusion</b><br />
Dans notre contexte, SFINAE n'a permis qu'une seule chose&nbsp;: générer un message d'erreur plus lisible si le <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">Processor</span> fourni n'a pas les fonctionnalités suffisantes. Mais ses possibilités sont nombreuses. À vous, maintenant que vous avez l'idée en tête, de faire preuve d'imagination&nbsp;! Au fur et à mesure que vous entrerez dans les subtilités de SFINAE, vous découvrirez aussi les subtilités du C++&nbsp;: savoir ce qui appartient au contexte d'évaluation (où tout doit être défini) et au contexte de compilation (où les définitions partielles sont permises) est une question byzantine. Vous pouvez jeter un œil sur <a href="http://en.cppreference.com/w/cpp/language/definition" target="_blank">cppreference </a> pour débroussailler le terrain. Vous verrez que tant qu'on reste en dehors de l'usage «&nbsp;odr&nbsp;» (comprendre one definition rule,) on reste dans les limites de ce qui peut être réalisé à la compilation.</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b1008/sfinae-interlude-cpp-avance/</guid>
		</item>
		<item>
			<title>Détection des associations fréquentes en C++ - troisième partie: une interface digne de ce nom</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b1003/detection-associations-frequentes-cpp-troisieme-partie-interface-digne-nom/</link>
			<pubDate>Mon, 04 Jan 2016 04:20:16 GMT</pubDate>
			<description>Après avoir introduit...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>Après avoir <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b965/detection-associations-frequentes-cpp-introduction/" target="_blank">introduit</a> l'algorithme de détection, j'en ai proposé une implémentation naïve (<a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b981/detection-associations-frequentes-cpp-premiere-partie/" target="_blank">1</a>, <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b994/detection-associations-frequentes-cpp-deuxieme-partie/" target="_blank">2</a>). En partant de si bas, beaucoup d'améliorations sont possibles avant d'en faire une implémentation de qualité. Nous commencerons par proposer <b>une interface compatible avec les exigences d'un développeur C++ client</b>.</i><br />
<b><br />
C'est une affaire de psychologie</b><br />
Le développeur C++ aime la performance et il déteste tout ce qui l'oblige à créer une structure de données intermédiaire, à allouer de la mémoire. Même copier un entier lui est désagréable. L'état d'esprit du développeur Python est différent: <i>si je peux créer</i>, pense-t-il, <i>en 20 minutes, un programme qui s'exécute en 1 seconde, je fais un meilleur marché que si je mets 20 heures à créer le même programme qui s'exécute en 1 ms</i>. Le développeur Python prend un plaisir particulier à écrire en deux lignes ce qui en prendrait 20 en C++. Pour cette raison, l'interface compte moins. <br />
<br />
Reprenons le fil de notre algorithme: l'argument de la fonction buildTree est <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::vector&lt;std::vector&lt;Item&gt;&gt;</span>; en python cela équivaut à une liste de listes: <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">[[]]</span>. Pour convertir en liste de listes un fichier où les transactions sont séparées par des retours à la ligne et les éléments des transactions par des espaces, il suffit d'écrire:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Python :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:36px;"><span class="br0">&#91;</span><span class="br0">&#91;</span>item <span style="color: #0000ff;">for</span> item <span style="color: #0000ff;">in</span> line.strip<span class="br0">&#40;</span><span class="br0">&#41;</span>.split<span class="br0">&#40;</span> <span class="br0">&#41;</span><span class="br0">&#93;</span> <span style="color: #0000ff;">for</span> line <span style="color: #0000ff;">in</span> open<span class="br0">&#40;</span><span style="color: #FF0000;">&quot;myfile.csv&quot;</span><span class="br0">&#41;</span><span class="br0">&#93;</span></pre>
</div><i>Et je me sens intelligent, moderne. J'ai une pensée entre amusement et pitié pour le développeur C++.</i><br />
<br />
<b>Pourquoi c'est mal, tentacule et csv</b><br />
En écrivant cette petite ligne de code, on a démontré la concision de Python, certes. On a aussi copié tout le fichier en mémoire, et on s'apprête à copier tous les objets en mémoire une seconde fois dans l'arbre des associations fréquentes. Quel développeur C++ digne de ce nom accepterait une chose pareille? <br />
<br />
Je vous conseille la lecture d'un <a href="http://www.developpez.net/forums/d1531366/c-cpp/cpp/lecture-csv-cpp-faire-faut/" target="_blank">excellent article de white_tentacle</a> qui porte sur la lecture d'un ficher csv en C++ et illustre bien cette exigence: pour travailler sur un fichier csv, il n'est pas toujours nécessaire de le charger entièrement en mémoire, loin de là; il faut toujours charger le minimum possible: peut-être une ligne, peut-être même simplement une cellule. Le parseur de csv que white_tentacle propose demande de fournir deux fonctions de call back: une appelée lorsqu'une cellule a été lue, une lorsqu'une ligne a été lue. S'il s'agit simplement d'afficher le fichier, par exemple, vous pourrez fournir une fonction qui affiche un élément comme call_back de cellule, et une fonction qui affiche un retour à la ligne comme call_back de ligne. Empreinte mémoire? quasi-nulle.<br />
<br />
Vous pouvez raisonner de la même façon pour une base de données: vous n'allez pas charger en mémoire tous les enregistrements qui correspondent à votre requête avant de travailler dessus, sauf si cela est tout à fait nécessaire; vous préfèrerez de travailler enregistrement par enregistrement, voire champ par champ, si cela est possible.<br />
<br />
<b>Une interface pour plaire à deux développeurs C++</b><br />
Vous devez donc proposer comme interface des fonctions<i> primitives</i>: celles qui donneront le plus de liberté au développeur client pour conserver une performance maximale. Mais ce n'est pas tout: vous devez également choisir une interface qui<b> vous</b> donnera suffisamment de liberté pour améliorer la performance de votre algorithme sans casser le code client. Il vous faut donc trouver le niveau de granularité adéquat: d'assez bas niveau pour laisser au client faire ses choix, d'assez haut niveau pour que vous puissiez modifier les vôtres. L'interface de départ, c'est tout le contraire:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:180px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td valign="top"><pre style="margin: 0">  <span style="color: #808080;">// une mauvaise interface</span>
  <span style="color: #808080;">// les fonctions pour construire la fp-tree</span>
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::vector&lt;Item&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceForFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceIntoTree<span style="color: black;">&#40;</span>std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #808080;">//les fonctions pour chercher les associations fr&eacute;quentes</span>
  <span style="color: #0000ff;">void</span> mineTree<span style="color: black;">&#40;</span>std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; patterns, <span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; prefix<span style="color: black;">&#41;</span>;
  std::vector&lt;Item&gt; ascendTree<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* bottom<span style="color: black;">&#41;</span>;
  std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; getConditionalPatterns<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><br />
<b>Quelle interface pour la construction du <i>frequent pattern tree</i>?</b><br />
Construire un arbre des associations fréquentes, c'est 1) parcourir la liste des transactions pour connaître la fréquence de chaque élément et 2) parcourir à nouveau la liste des transactions pour intégrer chacune des transactions à l'arbre. Contrairement à ce que laissait penser l'interface initiale, ce sont deux étapes hétérogènes: dans un cas c'est l'élément qui nous intéresse, dans le deuxième la séquence.<br />
<br />
La signature de <i>scanFrequency</i> doit donc se rapporter à un élément, plus à une séquence:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
  <span style="color: #0000ff;">void</span> scanFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i, <span style="color: #0000ff;">int</span> freq=<span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><br />
La signature de <i>scanPattern</i> ne peut pas non plus rester telle qu'elle était: elle prenait comme argument non pas une séquence mais une structure de données qui la représentait (le <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::vector&lt;Item&gt;</span>). Le moyen à privilégier pour représenter une séquence en C++, c'est l'itérateur:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">  <span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> RandomIterator&gt;
  <span style="color: #0000ff;">void</span> scanPattern<span style="color: black;">&#40;</span>RandomIterator b, RandomIterator e, <span style="color: #0000ff;">int</span> freq=<span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><br />
Il vaut mieux préciser dans la signature quel type d'itérateur est nécessaire. Ici, comme l'algorithme de tri de la STL est utilisé, vous avez besoin d'un <a href="http://www.cplusplus.com/reference/iterator/RandomAccessIterator/" target="_blank">itérateur permettant un accès aléatoire</a>. Le nom du paramètre en template n'est qu'une indication -précieuse- pour le client. Vous pouvez néanmoins ajouter une <a href="http://en.cppreference.com/w/cpp/language/static_assert" target="_blank">assertion statique</a> au début de la définition de la fonction pour générer un message d'erreur lisible lors de la compilation:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:132px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt; <span style="color: #808080;">// dans la d&eacute;finition d'une fonction template d'une classe template</span>
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> RandomIterator&gt; <span style="color: #808080;">// le template de la classe vient en premier</span>
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::scanPattern<span style="color: black;">&#40;</span>RandomIterator b, RandomIterator e, <span style="color: #0000ff;">int</span> freq<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">static_assert</span><span style="color: black;">&#40;</span>std::is_same&lt;
		     std::random_access_iterator_tag,
		     <span style="color: #0000ff;">typename</span> std::iterator_traits&lt;RandomIterator&gt;::iterator_category
		     &gt;::value, <span style="color: #FF0000;">&quot;Bad pattern iterator: access must be random in scanPattern(Iterator, Iterator)&quot;</span><span style="color: black;">&#41;</span>;
<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span></pre></td></tr></table></pre>
</div><br />
<b>Quelle interface pour extraire les <i>frequent patterns</i>?</b><br />
La signature de <i>mineTree</i> présente le même problème que celle de <i>scanPattern</i>: elle utilise une structure de données (passablement compliquée en plus) pour représenter la séquence des résultats. <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:36px;">void mineTree(std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, int&gt;&gt;&amp; patterns, const std::vector&lt;Item&gt;&amp; prefix);</pre>
</div>Néanmoins, la solution est moins évidente dans ce cas-là, car il n'y a pas d'itérateur qui soit à la fois assez général et d'usage assez fréquent pour représenter efficacement ce que la fonction attend de l'utilisateur. Mettons que l'on choisisse de nommer notre itérateur InserterIterator, l'utilisateur pourrait penser que <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::back_inserter</span>, <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::front_inserter</span> et <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">std::inserter</span>, indifféremment, vont fonctionner, ou bien il se demandera lequel est compatible. Il ne pensera pas nécessairement à un <a href="http://en.cppreference.com/w/cpp/iterator/ostream_iterator" target="_blank">std::ostream_iterator</a> qui pourrait pourtant être un candidat valable. Enfin, réaliser son propre itérateur est une tâche qui, sans être bien compliquée, ne doit pas être imposée à un client si elle n'est pas nécessaire.<br />
<br />
Vous me direz ce que vous en pensez: la solution la meilleure, à mon avis, est de demander à l'utilisateur de fournir quoique ce soit qui puisse être appelé comme une fonction: un foncteur, une fonction lambda, une simple fonction (ne les oublions pas!). J'ai donné à ce &quot;fonction-like&quot; le nom de Processor:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">  template &lt;class PatternProcessor&gt;
  void mineTree(PatternProcessor&amp; processor, const Prefix&amp; prefix = Prefix());</pre></td></tr></table></pre>
</div>Dans le corps de la fonction, au-lieu d'écrire:<br />
<span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">results.push_back(std::make_pair(last_transaction, last_transaction_frequency));</span>vous écrivez:<br />
<span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">processor(std::make_pair(last_transaction, last_transaction_frequency));</span>et c'est le seul changement.<br />
<br />
Quelques exemples d'appel:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:144px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
  <span style="color: #808080;">// pour affiche simplement le r&eacute;sultat &agrave; l'&eacute;cran</span>
  <span style="color: #0000ff;">using</span> Transaction = std::vector&lt;std::string&gt;;
  <span style="color: #0000ff;">using</span> FrequentPattern = std::pair&lt;Transaction, <span style="color: #0000ff;">int</span>&gt;;
  <span style="color: #0000ff;">auto</span> processor = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> FrequentPattern&amp; pat<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #808080;">// avec une lambda</span>
    <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; i : pat.first<span style="color: black;">&#41;</span> std::cout &lt;&lt; i &lt;&lt; <span style="color: #FF0000;">' '</span>;
    std::cout &lt;&lt; <span style="color: #FF0000;">&quot; : &quot;</span> &lt;&lt; pat.second &lt;&lt; std::endl;
  <span style="color: black;">&#125;</span>;
  fpt.mineTree<span style="color: black;">&#40;</span>processor<span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><br />
ou bien: <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
  <span style="color: #808080;">// pour remplir un vecteur</span>
  <span style="color: #0000ff;">using</span> Transaction = std::vector&lt;std::string&gt;;
  <span style="color: #0000ff;">using</span> FrequentPattern = std::pair&lt;Transaction, <span style="color: #0000ff;">int</span>&gt;;
  std::vector&lt;FrequentPattern&gt; rc;
  <span style="color: #0000ff;">auto</span> processor = <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> FrequentPattern&amp; pat<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> rc.push_back<span style="color: black;">&#40;</span>pat<span style="color: black;">&#41;</span>; <span style="color: black;">&#125;</span>
  fpt.mineTree<span style="color: black;">&#40;</span>processor<span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><b><br />
En résumé</b><br />
L'interface de la classe <i>fptree</i> ressemble désormais à ça:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:132px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
<span style="color: #0000ff;">public</span>:
  <span style="color: #0000ff;">void</span> scanFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i, <span style="color: #0000ff;">int</span> freq=<span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> RandomIterator&gt;
  <span style="color: #0000ff;">void</span> scanPattern<span style="color: black;">&#40;</span>RandomIterator b, RandomIterator e, <span style="color: #0000ff;">int</span> freq=<span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> PatternProcessor&gt;
  <span style="color: #0000ff;">void</span> mineTree<span style="color: black;">&#40;</span>PatternProcessor&amp; processor, <span style="color: #0000ff;">const</span> Prefix&amp; prefix = Prefix<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #0000ff;">const</span>;
  <span style="color: #0000ff;">void</span> show<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;</pre></td></tr></table></pre>
</div><br />
J'ai glissé sans le dire un <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">const</span> à la fin de la déclaration de <i>mineTree</i>. Un autre aspect important d'une interface C++ est de toujours préciser si une méthode modifie ou non la classe sur laquelle elle est appelée. Le respect de cette convention nommée <i><a href="https://isocpp.org/wiki/faq/const-correctness" target="_blank">const-correctness</a></i> permet au compilateur de faire du travail à notre place et, parfois, d'optimiser le code généré.<br />
<br />
<b>Seulement une étape</b><br />
Tout cela n'est bien sûr qu'une étape dans le processus de raffinement de notre classe. Mais nous pouvons désormais continuer ce travail à l'abri d'une interface qui n'énervera pas le développeur client et qui nous laisse une grande liberté pour modifier notre code.<br />
<br />
Dans le prochain épisode, vous mettrez vos mains dans le cambouis de la gestion mémoire...</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b1003/detection-associations-frequentes-cpp-troisieme-partie-interface-digne-nom/</guid>
		</item>
		<item>
			<title>Détection des associations fréquentes en C++ - deuxième partie</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b994/detection-associations-frequentes-cpp-deuxieme-partie/</link>
			<pubDate>Tue, 29 Dec 2015 09:55:58 GMT</pubDate>
			<description>Dans le billet précédent...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>Dans <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b981/detection-associations-frequentes-cpp-premiere-partie/" target="_blank">le billet précédent</a>, vous avez vu comment construire l'arbre des associations fréquentes, ou fptree. Vous verrez dans celui-ci <b>comment utiliser l'arbre pour déduire les associations fréquentes</b>, sans plus retourner à la base de données initiale. Vous terminerez ainsi de découvrir cette première implémentation naïve et &quot;pythonesque&quot; de l'algorithme. Dans les prochains billets, il sera temps de la raffiner pour arriver à un résultat plus conforme à l'esprit du C++ et de ses standards récents.</i><br />
<br />
<b>Extraction des associations fréquentes, le principe</b><br />
Comme je le mentionnais <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b965/detection-associations-frequentes-cpp-introduction/" target="_blank">en introduction</a>, l'extraction des associations fréquentes, quoique facile à encoder, est plus difficile à concevoir. Elle repose sur le principe suivant:<br />
<ul><li style="">on commence par placer les éléments fréquents de l'arbre, qui sont contenus dans la table des éléments, parmi les associations fréquentes. En effet, ils constituent trivialement des associations fréquentes;</li><li style="">pour chacun de ces éléments, en commençant par le moins fréquent, on va construire récursivement un arbre de fréquence conditionnel. Comment est-ce que cela fonctionne? </li></ul><br />
<br />
<b>L'arbre de fréquence conditionnel</b><br />
L'arbre de fréquence conditionnel d'un élément de l'arbre initial est généré à partir des séquences qui partent de chaque noeud contenant cet élément. La séquence commence à partir du parent de ce noeud et s'arrête au dernier noeud avant la racine. Voici la fonction qui recherche, pour un noeud, la séquence ascendante en question:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:144px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
std::vector&lt;Item&gt; fptree&lt;Item&gt;::ascendTree<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* bottom<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #808080;">// bottom est un des noeuds contenant l'&eacute;l&eacute;ment</span>
  std::vector&lt;Item&gt; res;                                                                       <span style="color: #808080;">// dont on veut cr&eacute;er l'arbre conditionnel </span>
  <span style="color: #0000ff;">while</span> <span style="color: black;">&#40;</span>bottom-&gt;parent != root<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    res.push_back<span style="color: black;">&#40;</span>bottom-&gt;parent-&gt;label<span style="color: black;">&#41;</span>;
    bottom = bottom-&gt;parent;
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">return</span> res;
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
Voici maintenant la fonction qui produit l'ensemble des séquences ascendantes. Une fréquence est attribuée à chacune des séquences: elle correspond à la fréquence du noeud dont on part.<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:168px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; fptree&lt;Item&gt;::getConditionalPatterns<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; res; <span style="color: #808080;">// un vector de paires &lt;s&eacute;quence, fr&eacute;quence&gt;</span>
  <span style="color: #0000ff;">auto</span> bnode = headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins; <span style="color: #808080;">// &agrave; partir de la table des &eacute;l&eacute;ments</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span>;;<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>                                           <span style="color: #808080;">// on parcourt tous les noeuds cousins</span>
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>!bnode<span style="color: black;">&#41;</span> <span style="color: #0000ff;">break</span>;
    res.push_back<span style="color: black;">&#40;</span>std::make_pair<span style="color: black;">&#40;</span>ascendTree<span style="color: black;">&#40;</span>bnode<span style="color: black;">&#41;</span>, bnode-&gt;count<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
    bnode = bnode-&gt;cousins; 
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">return</span> res;
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
<b>Redondance, redondance...</b><br />
La construction de l'arbre conditionnel est désormais semblable à la construction de l'arbre initial, à la nuance près que les séquences sont annotées d'une fréquence:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:132px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">bool</span> fptree&lt;Item&gt;::buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; kv : input<span style="color: black;">&#41;</span>
    scanSequenceForFrequency<span style="color: black;">&#40;</span>kv.first, kv.second<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span> kv : input<span style="color: black;">&#41;</span>
    scanSequenceIntoTree<span style="color: black;">&#40;</span>kv.first, kv.second<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">return</span> root-&gt;children;
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
Malgré les similitudes, on ne peut pas échapper à l'écriture d'une surcharge, à cause de mauvais choix initiaux; comparez la fonction au-dessus et la fonction en-dessous, utilisée pour créer l'arbre initial: <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:132px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">bool</span> fptree&lt;Item&gt;::buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::vector&lt;Item&gt;&gt;&amp; input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; vi : input<span style="color: black;">&#41;</span>
    scanSequenceForFrequency<span style="color: black;">&#40;</span>vi<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span> vi : input<span style="color: black;">&#41;</span> 
    scanSequenceIntoTree<span style="color: black;">&#40;</span>vi<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">return</span> root-&gt;children; <span style="color: #808080;">// si la racine a un enfant, l'arbre n'est pas vide</span>
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
On dirait du copier-coller, hein? Le père de tous les anti-patterns? hé bien oui.<br />
<br />
<b>Des arbres conditionnels aux associations fréquentes</b><br />
Vous passerez des arbres conditionnels aux associations fréquentes en construisant des préfixes. Lors du premier passage dans l'arbre, le préfixe est vide. Au deuxième passage, le préfixe est constitué de l'élément fréquent dont vous avez construit l'arbre conditionnel. Au troisième passage, on ajoute l'élément fréquent de l'arbre conditionnel dont vous construisez l'arbre conditionnel, et ainsi de suite, jusqu'à ce que l'arbre conditionnel soit vide. Le préfixe constitue une association fréquente: en effet, l'élément n du préfixe est fréquent dans l'arbre conditionnel de l'élément n-1 du préfixe.<br />
<br />
Pour explorer correctement l'ensemble de l'arbre, vous commencez l'extraction par l'élément le moins fréquent, dont les noeuds se trouvent le plus loin de la racine. Voici la fonction mineTree, qui extraie les associations fréquentes, et que vous pouvez désormais comprendre sans encombre:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::mineTree<span style="color: black;">&#40;</span>std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; patterns, <span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; prefix<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
&nbsp;
  <span style="color: #808080;">// retrieve items with mininum support </span>
  deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// retire de la headerTable (table des &eacute;l&eacute;ments)  les &eacute;l&eacute;ments qui ne sont pas assez fr&eacute;quents</span>
  std::vector&lt;Item&gt; keys;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; kv : headerTable<span style="color: black;">&#41;</span> keys.push_back<span style="color: black;">&#40;</span>kv.first<span style="color: black;">&#41;</span>; 
&nbsp;
  <span style="color: #808080;">// sort keys in increasing order of frequency</span>
  std::sort<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>keys<span style="color: black;">&#41;</span>, std::end<span style="color: black;">&#40;</span>keys<span style="color: black;">&#41;</span>, <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; a, <span style="color: #0000ff;">const</span> Item&amp; b<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count &lt; headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count; 
    <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #808080;">// get conditional patterns and create their fptrees</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; k : keys<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    std::vector&lt;Item&gt; nprefix = prefix;
    nprefix.push_back<span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span>; <span style="color: #808080;">// augment prefix with new key</span>
    patterns.push_back<span style="color: black;">&#40;</span>std::make_pair<span style="color: black;">&#40;</span>nprefix, headerTable<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span>.count<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
&nbsp;
    fptree&lt;Item&gt; cfpt<span style="color: black;">&#40;</span>minsup<span style="color: black;">&#41;</span>; <span style="color: #808080;">// recursively build new fptree</span>
    <span style="color: #0000ff;">bool</span> items_left = cfpt.buildTree<span style="color: black;">&#40;</span>getConditionalPatterns<span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>items_left<span style="color: black;">&#41;</span>
      cfpt.mineTree<span style="color: black;">&#40;</span>patterns, nprefix<span style="color: black;">&#41;</span>; <span style="color: #808080;">// recursively mine the new fptree</span>
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
<b>Ouf!</b><br />
Nous en avons terminé avec l'implémentation naïve. Maintenant nous allons pouvoir commencer le vrai travail, raffiner l'implémentation jusqu'à ce qu'elle soit agréable à utiliser et aussi performante et extensible que possible! Nous nous y attellerons dans le prochain billet!<br />
<br />
<b>A vous de jouer</b><br />
<ul><li style="">Quels sont, à votre avis, les fonctions indispensables qui constitueront l'interface minimale de notre structure de données?</li><li style="">Faut-il réduire une interface au minimum de fonctions requises pour exploiter la structure de données?</li></ul><br />
<br />
Pour expérimenter, voici in extenso les fichiers source:<br />
- fptree.h<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br />150<br />151<br />152<br />153<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #339933;">#ifndef __FP_FPTREE_H</span>
<span style="color: #339933;">#define __FP_TREE_H</span>
&nbsp;
<span style="color: #339933;">#include</span><span style="color: #FF0000;"> &quot;fpnode.hpp&quot;</span>
<span style="color: #339933;">#include</span><span style="color: #FF0000;"> &lt;algorithm&gt;</span>
<span style="color: #339933;">#include</span><span style="color: #FF0000;"> &lt;unordered_map&gt;</span>
<span style="color: #339933;">#include</span><span style="color: #FF0000;"> &lt;vector&gt;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">class</span> fptree <span style="color: black;">&#123;</span>
<span style="color: #0000ff;">public</span>:
  <span style="color: #808080;">// les donn&eacute;es de l'arbre</span>
  std::unordered_map&lt;Item, fpnode&lt;Item&gt;&gt; headerTable;
  fpnode&lt;Item&gt;* root;
  <span style="color: #0000ff;">int</span> minsup; <span style="color: #808080;">// la fr&eacute;quence minimale &agrave; respecter</span>
&nbsp;
  <span style="color: #808080;">//constructeur / destructeur</span>
  fptree<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span> sup<span style="color: black;">&#41;</span> : root<span style="color: black;">&#40;</span><span style="color: #0000ff;">new</span> fpnode&lt;Item&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, minsup<span style="color: black;">&#40;</span>sup<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
  ~fptree<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> deleteNode<span style="color: black;">&#40;</span>root<span style="color: black;">&#41;</span>;  <span style="color: black;">&#125;</span>
&nbsp;
  <span style="color: #808080;">// les fonctions pour construire la fp-tree</span>
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::vector&lt;Item&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceForFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceIntoTree<span style="color: black;">&#40;</span>std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #808080;">//les fonctions pour chercher les associations fr&eacute;quentes</span>
  <span style="color: #0000ff;">void</span> mineTree<span style="color: black;">&#40;</span>std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; patterns, <span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; prefix<span style="color: black;">&#41;</span>;
  std::vector&lt;Item&gt; ascendTree<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* bottom<span style="color: black;">&#41;</span>;
  std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; getConditionalPatterns<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #0000ff;">void</span> show<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;   
<span style="color: black;">&#125;</span>;
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::scanSequenceForFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; item : input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">auto</span> kv = headerTable.find<span style="color: black;">&#40;</span>item<span style="color: black;">&#41;</span>;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>kv != headerTable.end<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      kv-&gt;second.count += freq;
    <span style="color: black;">&#125;</span>
    <span style="color: #0000ff;">else</span> headerTable<span style="color: black;">&#91;</span>item<span style="color: black;">&#93;</span>.count = freq;
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  std::vector&lt;Item&gt; to_erase;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; kv : headerTable<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>kv.second.count &lt; minsup<span style="color: black;">&#41;</span> to_erase.push_back<span style="color: black;">&#40;</span>kv.first<span style="color: black;">&#41;</span>;
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; i : to_erase<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    headerTable.erase<span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span>;
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::scanSequenceIntoTree<span style="color: black;">&#40;</span>std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  input.erase<span style="color: black;">&#40;</span>std::remove_if<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
			     std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
			     <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.count &lt; minsup; <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>,
	      std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// delete elements under minimal freq support</span>
  std::sort<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
	    std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
	    <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; a, <span style="color: #0000ff;">const</span> Item&amp; b<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
	      <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count == headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count ? 
		std::less&lt;Item&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>a, b<span style="color: black;">&#41;</span> : 
		headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count &gt; headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count;
	    <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// most frequent elements first</span>
  fpnode&lt;Item&gt;* n = root;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; i : input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">auto</span> pair = n-&gt;addChild<span style="color: black;">&#40;</span>i, freq<span style="color: black;">&#41;</span>;
    n = pair.first;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>pair.second<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      n-&gt;cousins = headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins;
      headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins = n;
    <span style="color: black;">&#125;</span>
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
std::vector&lt;Item&gt; fptree&lt;Item&gt;::ascendTree<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* bottom<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  std::vector&lt;Item&gt; res;
  <span style="color: #0000ff;">while</span> <span style="color: black;">&#40;</span>bottom-&gt;parent != root<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    res.push_back<span style="color: black;">&#40;</span>bottom-&gt;parent-&gt;label<span style="color: black;">&#41;</span>;
    bottom = bottom-&gt;parent;
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">return</span> res;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; fptree&lt;Item&gt;::getConditionalPatterns<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; res;
  <span style="color: #0000ff;">auto</span> bnode = headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span>;;<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>!bnode<span style="color: black;">&#41;</span> <span style="color: #0000ff;">break</span>;
    res.push_back<span style="color: black;">&#40;</span>std::make_pair<span style="color: black;">&#40;</span>ascendTree<span style="color: black;">&#40;</span>bnode<span style="color: black;">&#41;</span>, bnode-&gt;count<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
    bnode = bnode-&gt;cousins;
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">return</span> res;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">bool</span> fptree&lt;Item&gt;::buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; kv : input<span style="color: black;">&#41;</span>
    scanSequenceForFrequency<span style="color: black;">&#40;</span>kv.first, kv.second<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span> kv : input<span style="color: black;">&#41;</span>
    scanSequenceIntoTree<span style="color: black;">&#40;</span>kv.first, kv.second<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">return</span> root-&gt;children;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">bool</span> fptree&lt;Item&gt;::buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::vector&lt;Item&gt;&gt;&amp; input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; vi : input<span style="color: black;">&#41;</span>
    scanSequenceForFrequency<span style="color: black;">&#40;</span>vi<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span> vi : input<span style="color: black;">&#41;</span> 
    scanSequenceIntoTree<span style="color: black;">&#40;</span>vi<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">return</span> root-&gt;children;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::mineTree<span style="color: black;">&#40;</span>std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; patterns, <span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; prefix<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
&nbsp;
  <span style="color: #808080;">// retrieve items with mininum support </span>
  deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;
  std::vector&lt;Item&gt; keys;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; kv : headerTable<span style="color: black;">&#41;</span> keys.push_back<span style="color: black;">&#40;</span>kv.first<span style="color: black;">&#41;</span>; 
&nbsp;
  <span style="color: #808080;">// sort in increasing order of frequency</span>
  std::sort<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>keys<span style="color: black;">&#41;</span>, std::end<span style="color: black;">&#40;</span>keys<span style="color: black;">&#41;</span>, <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; a, <span style="color: #0000ff;">const</span> Item&amp; b<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count &lt; headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count; 
    <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #808080;">// get conditional patterns and create their fptrees</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; k : keys<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    std::vector&lt;Item&gt; nprefix = prefix;
    nprefix.push_back<span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span>; <span style="color: #808080;">// augment prefix with new key</span>
    patterns.push_back<span style="color: black;">&#40;</span>std::make_pair<span style="color: black;">&#40;</span>nprefix, headerTable<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span>.count<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
&nbsp;
    fptree&lt;Item&gt; cfpt<span style="color: black;">&#40;</span>minsup<span style="color: black;">&#41;</span>; <span style="color: #808080;">// recursively build new fptree</span>
    <span style="color: #0000ff;">bool</span> items_left = cfpt.buildTree<span style="color: black;">&#40;</span>getConditionalPatterns<span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>items_left<span style="color: black;">&#41;</span>
      cfpt.mineTree<span style="color: black;">&#40;</span>patterns, nprefix<span style="color: black;">&#41;</span>;
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::show<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  root-&gt;show<span style="color: black;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span>;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #339933;">#endif</span></pre></td></tr></table></pre>
</div><br />
-fpnode.h<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #339933;">#ifndef __FP_NODE_H</span>
<span style="color: #339933;">#define __FP_NODE_H</span>
&nbsp;
<span style="color: #339933;">#include</span><span style="color: #FF0000;"> &lt;iostream&gt;</span>
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">struct</span> fpnode <span style="color: black;">&#123;</span>
  Item label;
  <span style="color: #0000ff;">int</span> count;
  fpnode *parent, *brothers, *children, *cousins;
&nbsp;
fpnode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> : label<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>, count<span style="color: black;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span>, parent<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span>, brothers<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span>, children<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span>, cousins<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>;
fpnode<span style="color: black;">&#40;</span>fpnode* father, <span style="color: #0000ff;">const</span> Item&amp; name, <span style="color: #0000ff;">int</span> freq, fpnode* bro<span style="color: black;">&#41;</span> : label<span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span>, count<span style="color: black;">&#40;</span>freq<span style="color: black;">&#41;</span>, parent<span style="color: black;">&#40;</span>father<span style="color: black;">&#41;</span>, brothers<span style="color: black;">&#40;</span>bro<span style="color: black;">&#41;</span>, children<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span>, cousins<span style="color: black;">&#40;</span><span style="color: #0080ff;">nullptr</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
  fpnode* hasChild<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; name<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #808080;">// return the name named child if found, nullptr otherwise</span>
    fpnode* n = children;
    <span style="color: #0000ff;">while</span> <span style="color: black;">&#40;</span>n<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>n-&gt;label == name<span style="color: black;">&#41;</span> <span style="color: #0000ff;">break</span>;
      n = n-&gt;brothers;
    <span style="color: black;">&#125;</span>
    <span style="color: #0000ff;">return</span> n;
  <span style="color: black;">&#125;</span>
&nbsp;
  std::pair&lt;fpnode*, <span style="color: #0000ff;">bool</span>&gt; addChild<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; name, <span style="color: #0000ff;">int</span> freq=<span style="color: #cc66cc;">0</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    fpnode* n = hasChild<span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span>;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>!n<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      children = <span style="color: #0000ff;">new</span> fpnode<span style="color: black;">&#40;</span><span style="color: #0000ff;">this</span>, name, freq, children<span style="color: black;">&#41;</span>;
      <span style="color: #0000ff;">return</span> std::make_pair<span style="color: black;">&#40;</span>children, <span style="color: #0080ff;">true</span><span style="color: black;">&#41;</span>;
    <span style="color: black;">&#125;</span>
    <span style="color: #0000ff;">else</span> n-&gt;count += freq;
    <span style="color: #0000ff;">return</span> std::make_pair<span style="color: black;">&#40;</span>n, <span style="color: #0080ff;">false</span><span style="color: black;">&#41;</span>;
  <span style="color: black;">&#125;</span>
&nbsp;
  <span style="color: #0000ff;">void</span> show<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span> offset<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    std::cout &lt;&lt; std::string<span style="color: black;">&#40;</span>offset, <span style="color: #FF0000;">' '</span><span style="color: black;">&#41;</span> &lt;&lt; label &lt;&lt; <span style="color: #FF0000;">&quot; - &quot;</span> &lt;&lt; count &lt;&lt; std::endl;
    <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* n = children; n; n=n-&gt;brothers<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      n-&gt;show<span style="color: black;">&#40;</span>offset+<span style="color: #cc66cc;">2</span><span style="color: black;">&#41;</span>;
    <span style="color: black;">&#125;</span>
  <span style="color: black;">&#125;</span>
&nbsp;
<span style="color: black;">&#125;</span>;
&nbsp;
<span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> deleteNode<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* n<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>n-&gt;children<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* ch = n-&gt;children; ch; ch = ch-&gt;brothers<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      deleteNode<span style="color: black;">&#40;</span>ch<span style="color: black;">&#41;</span>;
    <span style="color: black;">&#125;</span>
  <span style="color: black;">&#125;</span>
  <span style="color: #0000ff;">delete</span> n;
<span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #339933;">#endif</span></pre></td></tr></table></pre>
</div></blockquote>


<!-- attachments -->
	<div class="blogattachments">
		
		
			<fieldset class="blogcontent">
				<legend>Images attachées</legend>
				
			</fieldset>
		
		
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b994/detection-associations-frequentes-cpp-deuxieme-partie/</guid>
		</item>
		<item>
			<title>Détection des associations fréquentes en C++ - première partie</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b981/detection-associations-frequentes-cpp-premiere-partie/</link>
			<pubDate>Sat, 26 Dec 2015 16:15:45 GMT</pubDate>
			<description><![CDATA[Comme je l'indiquais dans le...]]></description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>Comme je l'indiquais dans le billet précédent, je propose maintenant une implémentation naïve, reprise presqu'exactement d'une implémentation en Python, de l'algorithme de détection des associations fréquentes centré autour d'une structure de donnée appelée frequent pattern tree, ou arbre des associations fréquentes. Il peut être utile de relire <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b965/detection-associations-frequentes-cpp-introduction/" target="_blank">la présentation de l'algorithme</a></i><br />
<b><br />
Une implémentation naïve</b><br />
Une implémentation en Python se ressent souvent de l'utilisation de ce langage pour créer des prototypes: plutôt que de chercher d'emblée à écrire une librairie bien finie qu'on pourra utiliser sans risque dans des programmes plus vastes, on cherche à mettre au point rapidement quelques briques logicielles à confronter à des données de test. Ni la performance, ni la sûreté ne sont recherchées prioritairement mais plutôt la simplicité et la réutilisation de la vaste librairie standard de ce langage &quot;piles incluses&quot;.<br />
<br />
Pour générer quelques données d'associations fréquentes, on fera donc une liste de listes; en C++ il faut ajouter un type mais grâce aux nouvelles <a href="http://fr.cppreference.com/w/cpp/utility/initializer_list" target="_blank">std::initializer_list</a> de C++11, on a des possibilités semblables:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:144px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td valign="top"><pre style="margin: 0">  std::vector&lt;std::vector&lt;std::string&gt;&gt; bseq = <span style="color: #808080;">// nos donn&eacute;es de test</span>
    <span style="color: black;">&#123;</span>
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;r&quot;</span>, <span style="color: #FF0000;">&quot;z&quot;</span>, <span style="color: #FF0000;">&quot;h&quot;</span>, <span style="color: #FF0000;">&quot;j&quot;</span>, <span style="color: #FF0000;">&quot;p&quot;</span> <span style="color: black;">&#125;</span>,
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;z&quot;</span>, <span style="color: #FF0000;">&quot;y&quot;</span>, <span style="color: #FF0000;">&quot;x&quot;</span>, <span style="color: #FF0000;">&quot;w&quot;</span>, <span style="color: #FF0000;">&quot;v&quot;</span>, <span style="color: #FF0000;">&quot;u&quot;</span>, <span style="color: #FF0000;">&quot;t&quot;</span>, <span style="color: #FF0000;">&quot;s&quot;</span> <span style="color: black;">&#125;</span>,
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;z&quot;</span><span style="color: black;">&#125;</span>,
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;r&quot;</span>, <span style="color: #FF0000;">&quot;x&quot;</span>, <span style="color: #FF0000;">&quot;n&quot;</span>, <span style="color: #FF0000;">&quot;o&quot;</span>, <span style="color: #FF0000;">&quot;s&quot;</span><span style="color: black;">&#125;</span>,
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;y&quot;</span>, <span style="color: #FF0000;">&quot;r&quot;</span>, <span style="color: #FF0000;">&quot;x&quot;</span>, <span style="color: #FF0000;">&quot;z&quot;</span>, <span style="color: #FF0000;">&quot;q&quot;</span>, <span style="color: #FF0000;">&quot;t&quot;</span>, <span style="color: #FF0000;">&quot;p&quot;</span><span style="color: black;">&#125;</span>,
      <span style="color: black;">&#123;</span><span style="color: #FF0000;">&quot;y&quot;</span>, <span style="color: #FF0000;">&quot;z&quot;</span>, <span style="color: #FF0000;">&quot;x&quot;</span>, <span style="color: #FF0000;">&quot;e&quot;</span>, <span style="color: #FF0000;">&quot;q&quot;</span>, <span style="color: #FF0000;">&quot;s&quot;</span>, <span style="color: #FF0000;">&quot;t&quot;</span>, <span style="color: #FF0000;">&quot;m&quot;</span><span style="color: black;">&#125;</span>
    <span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
<b>Une interface naïve</b><br />
L'interface de notre classe FP-Tree, naïvement, est conçue pour épouser la forme des données de test; pour pousser la ressemblance avec Python, l'encapsulation des données est laissée de côté...<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">class</span> fptree <span style="color: black;">&#123;</span>
<span style="color: #0000ff;">public</span>:
  <span style="color: #808080;">// les donn&eacute;es de l'arbre</span>
  std::unordered_map&lt;Item, fpnode&lt;Item&gt;&gt; headerTable;
  fpnode&lt;Item&gt;* root; <span style="color: #808080;">// la classe fpnode sera d&eacute;finie plus loin</span>
  <span style="color: #0000ff;">int</span> minsup; <span style="color: #808080;">// la fr&eacute;quence minimale &agrave; respecter</span>
&nbsp;
  <span style="color: #808080;">//constructeur / destructeur</span>
  fptree<span style="color: black;">&#40;</span><span style="color: #0000ff;">int</span> sup<span style="color: black;">&#41;</span> : root<span style="color: black;">&#40;</span><span style="color: #0000ff;">new</span> fpnode&lt;Item&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, minsup<span style="color: black;">&#40;</span>sup<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
  ~fptree<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> deleteNode<span style="color: black;">&#40;</span>root<span style="color: black;">&#41;</span>;  <span style="color: black;">&#125;</span>
&nbsp;
  <span style="color: #808080;">// les fonctions pour construire la fp-tree</span>
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::vector&lt;Item&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">bool</span> buildTree<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; input<span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceForFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> deleteInfrequentItems<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;
  <span style="color: #0000ff;">void</span> scanSequenceIntoTree<span style="color: black;">&#40;</span>std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq = <span style="color: #cc66cc;">1</span><span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #808080;">//les fonctions pour chercher les associations fr&eacute;quentes</span>
  <span style="color: #0000ff;">void</span> mineTree<span style="color: black;">&#40;</span>std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt;&amp; patterns, <span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; prefix<span style="color: black;">&#41;</span>;
  std::vector&lt;Item&gt; ascendTree<span style="color: black;">&#40;</span>fpnode&lt;Item&gt;* bottom<span style="color: black;">&#41;</span>;
  std::vector&lt;std::pair&lt;std::vector&lt;Item&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; getConditionalPatterns<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span>;
&nbsp;
  <span style="color: #0000ff;">void</span> show<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>;   
<span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
Sans analyser trop précisément cette interfacte &quot;pythonesque&quot;, plusieurs choses sautent aux yeux:<br />
- pas d'encapsulation des données, impossible de changer quoique ce soit une fois la librairie offerte au vaste monde...<br />
- une interface beaucoup trop grosse. Impossible de s'y repérer aisément, de la comprendre facilement<br />
- beaucoup de structures de données figées; même s'il est devenu beaucoup plus facile en C++11 d'itérer sur un conteneur et donc de construire un conteneur d'un autre type avec, on sent que l'interface sera contraignante dès que le scénario retenu pour prototyper la librairie ne sera pas respecté<br />
<br />
<b>Pour l'instant, tout va bien</b><br />
Malgré tous ces défauts, pour chercher les associations fréquentes dans notre scénario idéal, il suffit de faire:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:84px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br /></div></td><td valign="top"><pre style="margin: 0">fptree&lt;std::string&gt; fpt<span style="color: black;">&#40;</span><span style="color: #cc66cc;">3</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// fr&eacute;quent = au moins trois occurrences</span>
fpt.buildTree<span style="color: black;">&#40;</span>bseq<span style="color: black;">&#41;</span>; <span style="color: #808080;">// bseq = les donn&eacute;es d&eacute;finies &agrave; l'instant</span>
std::vector&lt;std::pair&lt;std::vector&lt;std::string&gt;, <span style="color: #0000ff;">int</span>&gt;&gt; fpatterns; <span style="color: #808080;">// la structure pour accueillir les r&eacute;sultats</span>
fpt.mineTree<span style="color: black;">&#40;</span>fpatterns, std::vector&lt;std::string&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// on lance l'analyse des associations fr&eacute;quentes</span></pre></td></tr></table></pre>
</div><br />
Le résultat est le suivant:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
<span style="color: #808080;">// toutes les associations apparaissant plus de trois fois, avec leur nombre d'occurrences</span>
s : <span style="color: #cc66cc;">3</span> <span style="color: #808080;">// pour &ecirc;tre pr&eacute;cis, c'est l'association d'un &eacute;l&eacute;ment et de l'&eacute;l&eacute;ment nul</span>
s x : <span style="color: #cc66cc;">3</span>
t : <span style="color: #cc66cc;">3</span>
t z : <span style="color: #cc66cc;">3</span>
t z x : <span style="color: #cc66cc;">3</span>
t x : <span style="color: #cc66cc;">3</span>
y : <span style="color: #cc66cc;">3</span>
y z : <span style="color: #cc66cc;">3</span>
y z t : <span style="color: #cc66cc;">3</span>
y z x : <span style="color: #cc66cc;">3</span>
y z x t : <span style="color: #cc66cc;">3</span>
y x : <span style="color: #cc66cc;">3</span>
y x t : <span style="color: #cc66cc;">3</span>
y t : <span style="color: #cc66cc;">3</span>
r : <span style="color: #cc66cc;">3</span>
x : <span style="color: #cc66cc;">4</span>
x z : <span style="color: #cc66cc;">3</span>
z : <span style="color: #cc66cc;">5</span></pre></td></tr></table></pre>
</div><br />
La fonction show permet de visualiser l'arbre, même si d'évidence il faudrait faire mieux:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:192px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td valign="top"><pre style="margin: 0"> - <span style="color: #cc66cc;">0</span> <span style="color: #808080;">// la racine</span>
  x - <span style="color: #cc66cc;">1</span> <span style="color: #808080;">// plus la ligne est indent&eacute;e, plus l'&eacute;l&eacute;ment est loin de la racine</span>
    r - <span style="color: #cc66cc;">1</span>
      s - <span style="color: #cc66cc;">1</span>
  z - <span style="color: #cc66cc;">5</span>
    x - <span style="color: #cc66cc;">3</span>
      r - <span style="color: #cc66cc;">1</span>
        t - <span style="color: #cc66cc;">1</span>
          y - <span style="color: #cc66cc;">1</span>
      s - <span style="color: #cc66cc;">2</span>
        t - <span style="color: #cc66cc;">2</span>
          y - <span style="color: #cc66cc;">2</span>
    r - <span style="color: #cc66cc;">1</span></pre></td></tr></table></pre>
</div><br />
<b>Un arbre familial</b><br />
Pour rentrer un peu plus avant dans l'implémentation, il faut présenter les noeuds de l'arbre. Comme l'arbre s'appelle fptree, les noeuds s'appellent fpnode:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">struct</span> fpnode <span style="color: black;">&#123;</span>
  Item label;
  <span style="color: #0000ff;">int</span> count; <span style="color: #808080;">// la fr&eacute;quence</span>
  fpnode *parent, *brothers, *children, *cousins; <span style="color: #808080;">// toute la famille</span>
<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span>
<span style="color: black;">&#125;</span>;</pre></td></tr></table></pre>
</div><br />
Chaque noeud a:<br />
- un parent;<br />
- des enfants (children): en fait le pointeur pointe sur l'aîné des enfants; pour retrouver les autres enfants, il faut suivre le pointeur qui part de l'aîné vers ses frères (brothers);<br />
- des frères, donc;<br />
- des cousins: ce sont les autres éléments de l'arbre qui ont le même label.<br />
<br />
<b>Ajouter une séquence d'éléments à l'arbre</b><br />
Comme je l'expliquais dans le billet précédent, il y a deux grandes étapes dans la construction d'une FP-Tree. La première consiste à parcourir tous les éléments de toutes les séquences pour déterminer leur fréquence. Il n'y a pas de difficulté particulière:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:156px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::scanSequenceForFrequency<span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; item : input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">auto</span> kv = headerTable.find<span style="color: black;">&#40;</span>item<span style="color: black;">&#41;</span>;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>kv != headerTable.end<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #808080;">// si l'&eacute;l&eacute;ment est d&eacute;j&agrave; connu</span>
      kv-&gt;second.count += freq; <span style="color: #808080;">// on augmente sa fr&eacute;quence</span>
    <span style="color: black;">&#125;</span>
    <span style="color: #0000ff;">else</span> headerTable<span style="color: black;">&#91;</span>item<span style="color: black;">&#93;</span>.count = freq; <span style="color: #808080;">// sinon on l'ajoute &agrave; la table</span>
  <span style="color: black;">&#125;</span>
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
Lorsque la fréquence de tous les éléments est connue, on peut ajouter des séquences à l'arbre proprement dit, à partir de la racine. C'est le rôle de la fonction:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">template</span> &lt;<span style="color: #0000ff;">class</span> Item&gt;
<span style="color: #0000ff;">void</span> fptree&lt;Item&gt;::scanSequenceIntoTree<span style="color: black;">&#40;</span>std::vector&lt;Item&gt;&amp; input, <span style="color: #0000ff;">int</span> freq<span style="color: black;">&#41;</span></pre></td></tr></table></pre>
</div><br />
Ne seront ajoutés que les éléments fréquents; pour que l'arbre soit le plus compressé possible, il faut ajouter les éléments de la séquence en commençant par les plus fréquents:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:180px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
  input.erase<span style="color: black;">&#40;</span>std::remove_if<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
			     std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
			     <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; i<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.count &lt; minsup; <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>,
	      std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// enlever &eacute;l&eacute;ments non fr&eacute;quents</span>
  std::sort<span style="color: black;">&#40;</span>std::begin<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
	    std::end<span style="color: black;">&#40;</span>input<span style="color: black;">&#41;</span>, 
	    <span style="color: black;">&#91;</span>&amp;<span style="color: black;">&#93;</span><span style="color: black;">&#40;</span><span style="color: #0000ff;">const</span> Item&amp; a, <span style="color: #0000ff;">const</span> Item&amp; b<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
	      <span style="color: #0000ff;">return</span> headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count == headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count ? 
		std::less&lt;Item&gt;<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>a, b<span style="color: black;">&#41;</span> : 
		headerTable<span style="color: black;">&#91;</span>a<span style="color: black;">&#93;</span>.count &gt; headerTable<span style="color: black;">&#91;</span>b<span style="color: black;">&#93;</span>.count;
	    <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>; <span style="color: #808080;">// les plus fr&eacute;quents d'abord + cl&eacute; secondaire</span></pre></td></tr></table></pre>
</div><b>NB</b>: j'ai utilisé de fonctions lambda, qui permettent de tirer parti des algorithmes de la bibliothèque standard de façon agréable. Ce sont des petites fonctions, familières dans un langage comme Python, définies localement et que l'on passe facilement en argument à d'autres fonctions.<br />
<br />
On peut alors ajouter la séquence à l'arbre. L'ajout se fait à la racine, élément par élément, de façon récursive: si la racine a un enfant nommé comme le premier élément, on en augmente la fréquence; sinon, on crée un nouvel enfant pour la racine. Et on recommence l'opération sur le noeud obtenu. <br />
<br />
Dès qu'on crée un nouveau noeud il faut l'indexer dans la headerTable: cette liste des cousins permettra de retrouver tous les chemins partant d'un des éléments vers la racine lorsque nous rechercherons les associations fréquentes.<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0">  fpnode&lt;Item&gt;* n = root;
  <span style="color: #0000ff;">for</span> <span style="color: black;">&#40;</span><span style="color: #0000ff;">auto</span>&amp; i : input<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    <span style="color: #0000ff;">auto</span> pair = n-&gt;addChild<span style="color: black;">&#40;</span>i, freq<span style="color: black;">&#41;</span>;
    n = pair.first;
    <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>pair.second<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> <span style="color: #808080;">// pair second == true &lt;=&gt; un nouveau noeud a &eacute;t&eacute; cr&eacute;&eacute;</span>
      n-&gt;cousins = headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins; <span style="color: #808080;">// on met &agrave; jour la table des &eacute;l&eacute;ments</span>
      headerTable<span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.cousins = n;</pre></td></tr></table></pre>
</div><b>NB</b>: deux &quot;features&quot; nouvelles de C++11 sont utilisées ici: <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">auto</span> pour déduire le type d'une variable à initialiser et la construction <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">(for element : conteneur) { ... }</span> qui équivaut à: <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0">for (std::conteneur::iterator it = std::begin(conteneur); it != std::end(conteneur); ++it) {
  conteneur::value_type element = *it; 
  ... }</pre></td></tr></table></pre>
</div>Pensez à utiliser <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">auto<b>&amp;</b></span> si vous voulez éviter de copier les élément du conteneur.<br />
<br />
Une fois l'arbre construit, il devient possible de chercher les associations fréquentes sans plus retourner à la base de données. Nous verrons cela dans la prochain billet mais en attendant, joyeux Noël! :lahola:<br />
<i><br />
A suivre...</i></blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b981/detection-associations-frequentes-cpp-premiere-partie/</guid>
		</item>
		<item>
			<title>Détection des associations fréquentes en C++ - Introduction</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b965/detection-associations-frequentes-cpp-introduction/</link>
			<pubDate>Wed, 23 Dec 2015 13:20:12 GMT</pubDate>
			<description>Pour ceux qui ont suivi ce...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>Pour ceux qui ont suivi ce blog dans une vie antérieure, ce billet n'a aucun rapport avec les précédents. J'ai échoué à susciter le débat autour de la programmation fonctionnelle, dont acte. La série de billets que je prépare portera sur l'élaboration d'<b>une petite bibliothèque d'apprentissage machine en C++ moderne</b> en essayant de faire ressortir <b>la singularité de ce langage</b>. Il serait peut-être plus avisé de créer un autre blog mais la règle de developpez.net semble être: un utilisateur, un blog, et je m'y plie bien volontiers. Cela dit, commençons.</i><br />
<br />
<b>Les associations fréquentes, ou frequent patterns</b><br />
Le premier algorithme que nous développerons devra permettre, dans un ensemble de séquences, de détecter les associations d'éléments les plus fréquentes. Imaginez que vous êtes embauchés par votre libraire: il veut pouvoir suggérer d'autres lectures à ses clients lorsqu'ils passent à la caisse et vous demande donc le moyen de savoir quels sont les livres fréquemment achetés ensemble... Et vous voilà à chercher des associations! Voici deux autres exemples de l'utilisation qu'on peut en faire:<br />
<br />
- <u>proposer une saisie dans un moteur de recherche</u>: lorsque vous tapez votre recherche, le moteur propose souvent de la compléter; ses propositions sont tirées des associations les plus fréquentes dans les recherches précédentes. Donc, si vous voulez vous faire une idée de ce que pense le sexe opposé à votre sujet, tapez: &quot;Pourquoi les hommes&quot; ou &quot;Pourquoi les femmes&quot; et le moteur vous proposera des recherches avisées, comme: &quot;Pourquoi les hommes adorent les chieuses&quot;, &quot;Pourquoi les hommes mentent&quot;, etc. Ce n'est pas que le moteur a un avis sur la question, c'est juste que c'est une question fréquemment posée. <br />
<br />
- <u>fixer la disposition des rayons et les prix dans un magasin</u>: en prenant la liste des achats de vos clients, et en détectant les associations fréquentes, vous pouvez décider de rapprocher -ou d'éloigner- des produits, voire d'en fixer les prix. L'exemple le plus célèbre est celui des bières et des couches: un magasin américain avait remarqué, par cette méthode, que ses clients achetaient souvent ensemble bières et couches; ils en avaient déduit que les maris envoyés faire les courses pour les enfants se dédommageaient de leur peine en prenant quelques bières. Moralité, les bières -plein tarif- ont été placées à côté des couches, pour encourager à la consommation.<br />
<br />
On pourrait réfléchir à bien d'autres façons d'utiliser les associations fréquentes: faire le menu d'un restaurant, améliorer l'interface d'un logiciel, etc.<br />
<br />
<b>Qu'est-ce qu'un association fréquente?</b><br />
Essayons de donner une définition plus rigoureuse des associations fréquentes: soit un ensemble d'éléments que nous appellerons E. Soit des séquences, ou transactions, qui sont des sous-ensembles de E. Une association fréquente est un sous-ensemble de E qui est également un sous-ensemble d'un nombre déterminé (fréquent) de séquences.<br />
Il y a deux façons de définir le caractère fréquent d'une association: on peut fixer un seuil d'apparition, 10 apparitions, par exemple, ou bien un pourcentage: si l'élément apparaît dans au moins 5% des séquences analysées, on considérera qu'il est fréquent. Néanmoins, une fois que l'on connaît le nombre de séquences à analyser, la deuxième définition est réductible à la première. <br />
<br />
<b>Quels sont les algorithmes utilisés?</b><br />
<i>La force brute</i><br />
ne convient que pour les petites bases de données. On génère d'abord l'ensemble des éléments apparaissant dans la base de données, puis tous ses sous-ensembles, et on vérifie la fréquence de chacun des sous-ensembles dans la base de données. Mettons que vous ayez 99 articles dans votre magasin, vous aurez 2<sup>100</sup> sous-ensembles à vérifier. C'est long. Si vous êtes un moteur de recherche, et même si vous avez la puissance de calcul de Google, imaginez le nombre de sous-ensembles et reconnaissez que vous êtes foutus.<br />
<br />
<i>L'algorithme <u>a priori</u></i><br />
L'algorithme appelé a priori repose sur une propriété évidente des associations fréquentes: si un élément, ou un sous-ensemble, n'est pas fréquent, aucun des ensembles qui le contient ne le sera. Si le sous-ensemble { &quot;femme&quot;, &quot;simple&quot; } n'est pas fréquent, alors { &quot;femme&quot;, &quot;simple&quot;, &quot;caractère&quot;, &quot;égal&quot; } ne le sera pas non plus.<br />
<br />
Que peut-on en tirer d'un point de vue algorithmique (je vous laisse en tirer les conclusions que vous voulez pour votre vie sentimentale)? Si un sous-ensemble n'est pas fréquent, il n'est pas nécessaire de vérifier les sous-ensembles qui le contiennent; c'est ce que fait l'algorithme <i>a priori</i>: on commence par générer tous les sous-ensembles d'un seul élément - on raye les sous-ensembles qui n'atteignent pas le seuil requis - on combine les sous-ensembles restant pour former des sous-ensembles de deux éléments - on raye ceux qui n'ont pas la fréquence requise - on combine pour obtenir des ensembles à trois éléments, et ainsi de suite, jusqu'à épuisement.<br />
<br />
Quoiqu'il réduise le nombre de passage dans la base de données, il reste nécessaire de scanner la base de données pour chaque combinaison, ce qui peut être un gros désavantage dès que la connexion/la lecture est lente.<br />
<br />
<i>L'arbre des associations fréquentes, ou frequent pattern tree</i><br />
<b>C'est l'algorithme que nous implémenterons ensemble et longuement</b>. C'est un algorithme nettement plus compliqué mais nettement plus efficace. Vous pouvez jeter un oeil à l'article original <a href="http://scholar.google.fr/scholar_url?url=http://www.dei.unipd.it/~capri/DATAMINING/PAPERS/HanPY00.pdf.gz&amp;hl=fr&amp;sa=X&amp;scisig=AAGBfm20l01nCqxJVryB26Rfd0yFFJOPnQ&amp;nossl=1&amp;oi=scholarr&amp;ved=0ahUKEwiF2Yuq7PHJAhUD6RQKHTHMC88QgAMIICgBMAA" target="_blank">ici</a> (c'est un pdf dans une archive), ou taper &quot;frequent pattern tree&quot; dans votre moteur de recherche favori, vous le trouverez facilement. Il promet d'être, pour la plupart des données à analyser, plus rapide de plusieurs ordres de grandeur. Son premier avantage est de ne demander que deux passages dans la base de données: le premier pour déterminer les éléments fréquents, le deuxième pour construire l'arbre lui-même. Toutes les opérations suivantes sont effectuées sur l'arbre, qui est une version réduite et pratique de la base de données<br />
<br />
L'AAF est composé de deux élements principaux:<br />
- un arbre qui contient les séquences de la base de données triées et condensées<br />
- une table qui permet de retrouver dans l'arbre tous les noeuds qui renvoient au même élément.<br />
<br />
En voici un exemple (avec une fréquence minimum de 1, ce qui n'est pas très représentatif, certes), illustrant mes capacités en dessin. Il est extrait de la base de données suivante:<br />
<br />
s1: { eau }<br />
s2: { eau, bière, couches }<br />
s3: { eau, bière, chips}<br />
s4: { eau }<br />
s5: { bière, vodka }<br />
s6: { eau, bière, couches }<br />
<img src="https://www.developpez.net/forums/attachments/p196196d1450876141/c-cpp/outils-c-cpp/cppbuilder/pb-fenetre-mdi/fptree.png/" border="0" alt="Nom : fptree.png
Affichages : 3248
Taille : 32,2 Ko"  style="float: CONFIG" /><br />
<b>Les grandes étapes de la construction de l'AAF</b><br />
1. scannez la base de données pour obtenir, pour chaque élément présent, sa fréquence et construisez partiellement la table avec.<br />
2. scannez à nouveau la base de données, et pour chaque séquence:<br />
2.1: retirer les éléments qui ne sont pas assez fréquents<br />
2.2: triez-les dans l'ordre <b>décroissant</b> de fréquence<br />
2.3: ajoutez-les à l'arbre: ne créez un nouveau noeud que si la séquence ajoutée ne se confond pas avec une séquence précédente; sinon augmentez simplement la fréquence du noeud: dans l'exemple que j'ai donné, { eau } crée un nouveau noeud car l'arbre était vide; { eau, bière, couches } crée deux noeuds puisque son premier élément était déjà à la bonne place dans l'arbre; { eau, bière, chips } crée un seul nouveau noeud puisque les deux premiers éléments étaient à la bonne place, etc. <br />
<i>Voilà, votre arbre est prêt.</i><br />
<br />
<b>Trouver les associations fréquentes</b><br />
C'est à la fois plus simple à faire et plus compliqué à concevoir; la détection tire partie des propriétés de l'AAF.<br />
1. On reprend les éléments fréquents dans la table et on les trie dans l'ordre <b>croissant </b>de fréquence<br />
2. Pour chacun de ces éléments:<br />
2.1: on ajoute l'élément, avec un préfixe initialement vide, à la liste des associations fréquentes // le sens du préfixe se comprend en 2.3<br />
2.2 on prend l'ensemble des chemins qui partent des noeuds contenant cet élément et vont vers la racine de l'arbre<br />
2.3 on crée une nouvelle AAF avec ces chemins et on recommence en ajoutant l'élément au préfixe utilisé en 2.1<br />
<i>Voilà, vos associations sont trouvées</i><br />
<br />
<b>Le programme pour la suite</b><br />
Mon projet n'est pas seulement d'implémenter l'algorithme en C++. Je proposerai dans le prochain billet une interprétation naïve, qui est la transcription en C++ d'une implémentation en Python que j'ai trouvé dans l'excellent <a href="https://www.manning.com/books/machine-learning-in-action" target="_blank">Machine learning in Action</a> de Peter Harrington. En partant de ce prototype grossier, nous raffinerons ensemble -j'espère avoir en commentaire des propositions- la conception et l'implémentation de l'algorithme dans un esprit C++.</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b965/detection-associations-frequentes-cpp-introduction/</guid>
		</item>
		<item>
			<title>Analyse syntaxique monadique</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b490/analyse-syntaxique-monadique/</link>
			<pubDate>Thu, 07 May 2015 15:40:07 GMT</pubDate>
			<description>Continuons à explorer les...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Continuons à explorer les flots de la théorie des catégories. Le <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b467/jack-bauer-monades/" target="_blank">précédent billet</a> reposait sur un exemple très simple et très artificiel. L’ambition de celui-là est de proposer des techniques utilisables au jour le jour par un développeur et fondées sur l’utilisation des catégories.<br />
<br />
<b>Analyses</b><br />
L’analyse de texte est une de ces tâches de tous les jours. Par texte, j’entends un flux de données (caractères, octets, peu importe) qui suit des règles formalisées par un langage. Certains de ces langages sont peu ambitieux, comme le format csv ; et d’autres le sont extrêmement, comme le C++. Par analyse, j’entends la transformation de ce flux brut en un résultat structuré et exploitable.<br />
<br />
Sous forme d’explication d’une « perle fonctionnelle » dont l’article original est disponible <a href="https://www.cs.nott.ac.uk/~gmh/pearl.pdf" target="_blank">ici</a>, j’espère convaincre que les catégories peuvent offrir des outils de tous les jours, commodes et puissants à la fois.<br />
<br />
<b>Description naïve et fonctionnelle</b><br />
Essayons de décrire la tâche à accomplir : nous avons un flux, nous en extrayons des données. Cela fait déjà deux types, mettons<i> s</i> pour le flux et <i>a</i> pour la donnée à extraire. Et bien sûr un troisième type,<i> s -&gt; a</i>, qui est la fonction d’extraction. Cette première approche pourrait être retenue dans un langage impératif :<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:84px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br /></div></td><td valign="top"><pre style="margin: 0">&nbsp;
<span style="color: #0000ff;">typedef</span> <span style="color: #0000ff;">int</span> Data ;
<span style="color: #0000ff;">typedef</span> std::string Stream ;
Data parseInt<span style="color: black;">&#40;</span>Stream&amp; s<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> &#133; <span style="color: black;">&#125;</span> <span style="color: #808080;">// retourne le premier int d&eacute;celable et retire du flux les caract&egrave;res qui le composent</span></pre></td></tr></table></pre>
</div><br />
Néanmoins elle repose sur la possibilité de modifier le flux d’entrée pour analyser le prochain entier. Outre que c’est impossible dans un langage fonctionnel pur comme Haskell, ce n’est même pas désirable, puisqu’on pourrait souhaiter appliquer deux analyseurs différents au même « moment » du flux et décider en fonction du résultat, par exemple.<br />
<br />
Transformons un peu notre troisième type pour régler le problème: un analyseur fonctionnel prend un flux en entrée et retourne un couple (donnée, flux); ainsi le flux d’entrée est conservée à l’identique et le flux « consommé » est intégré au type de retour. Voici donc le type transformé: <i>s -&gt; (a, s)</i>.<br />
<br />
Comme un analyseur peut retourner zéro ou plusieurs réponses (dans le cas d’une grammaire ambiguë, par exemple) il faut transformer encore un peu notre type; nous parvenons à : <i>s -&gt; [(a, s)]</i>. Aucune réponse, l’analyseur renvoie la liste vide ; plusieurs, il renvoie la liste des candidats.<br />
<br />
<b>Notre monade</b><br />
Nous avons donc défini le type de notre analyseur. En choisissant une chaîne de caractère pour représenter le flux, par souci de simplification, nous avons :<br />
<div style="margin-left:40px"><font color="#000080">newtype Parser a = Parser (String -&gt; [(a, String)])</font></div><br />
<i>N.B : newtype est une directive qui permet de recouvrir un type, en lui donnant un autre nom. Contrairement à un alias (ex: typedef), elle crée bien un nouveau type, sans conversion implicite possible. </i><br />
<br />
Comme nous ne pouvons plus lancer directement notre analyseur, nous créons une fonction d’appoint qui lance l’analyseur :<div style="margin-left:40px"><font color="#000080">parse :: (Parser a) -&gt; (String -&gt; [(a, String)])<br />
parse (Parser p) = p</font></div><br />
il est possible dès lors de lancer l’analyseur de la façon suivante :<div style="margin-left:40px"><font color="#4b0082">&gt; parse parseur flux</font></div><br />
Prenons l’analyseur le plus simple possible, qui extrait un caractère d’une chaîne :<div style="margin-left:40px"><font color="#000080">item :: Parser Char<br />
item = Parser (\cs -&gt; case cs of     <br />
<div style="margin-left:40px">(c:nc) -&gt; [(c, nc)]<br />
                _ -&gt; [])</div></font></div> <i>N.B : case x of <br />
<div style="margin-left:40px">Cas1 -&gt; rep1<br />
Cas2 -&gt; rep2</div>est l’équivalent d’un switch qui permet le pattern-matching</i><br />
<br />
<div style="margin-left:40px"><font color="#4b0082">&gt; parse item &quot;abc&quot;</font><br />
<font color="#8B4513">[(a, &quot;bc&quot;)]</font></div><br />
<b>Rencontre du quatrième type</b><br />
Il nous reste un dernier type à explorer, peut-être le plus important. Dans notre exemple naïf, la fonction parse contenait les différentes informations nécessaires à la création de l’analyseur: la liste des caractères acceptables pour fournir un entier, le nombre de caractères à lire ou le point d’arrêt à attendre, les mesures à prendre en cas d’échec, etc. Pour peu qu’on extraie cette logique des fonctions codées « en dur », on pourra créer à loisir, par modification, enrichissement, composition, autant d’analyseurs qu’on le souhaite à partir de quelques primitives.<br />
<br />
Le quatrième type est donc celui d’un « constructeur » d’analyseurs, qui associe à un paramètre quelconque (une liste, un caractère, une fonction, etc.) un analyseur, soit <i>a -&gt; Parser b</i>.<br />
<br />
Ce quatrième type est utilisé pour définir les méthodes monadiques, composition et identité, du type Parser.<br />
<br />
<b>Identité et composition</b><br />
La fonction identité a justement pour signature : <i>a -&gt; Parser a</i>. L’idée derrière l’identité est d’injecter a dans la monade mais en ajoutant le minimum de contexte monadique possible. Elle est donc simplement définie par : <br />
<div style="margin-left:40px"><font color="#000080">return a = Parser (\cs -&gt; [(a, cs)])</font></div><br />
C’est-à-dire qu’elle renvoie un parseur qui laisse le flux inchangé et renvoie la donnée en argument.<br />
<br />
La composition demande plus de réflexion :<br />
<br />
<font color="#000080"><div style="margin-left:40px">(&gt;&gt;=) :: Parser a -&gt; (a -&gt; Parser b) -&gt; Parser b<br />
p &gt;&gt;= f  = Parser (\cs -&gt; concat [parse (f a) cs' | (a, cs') &lt;- parse p cs])</div></font><br />
<br />
Cette définition très opaque demande à être explicitée :<br />
- l’argument de concat est une list comprehension : <br />
- 1) pour chacun des résultats (a, cs’) qui proviennent de l’analyse de cs par p, <br />
- 2) on applique f :: <i>a -&gt; Parser b</i> à a et <br />
- 3) on analyse cs’ avec le(s) Parser(s) ainsi obtenus.<br />
- Cette list comprehension produit donc une liste de listes de couples (a, s) ; concat permet d’en faire une seule liste (concat :: <i>[[a]] -&gt; [a]</i>)<br />
Cette définition permet ainsi de composer deux analyseurs qui peuvent renvoyer une pluralité de résultats.<br />
<br />
<b>Enrichir la monade</b><br />
Lorsqu’on regarde la définition de la composition, on s’aperçoit que, si l’analyseur p échoue, c’est-à-dire renvoie la liste vide, la fonction f ne sera jamais appelée. Un analyseur qui retourne une liste vide a donc des propriétés particulières, semblables à celle de 0 pour la multiplication :<br />
<div style="margin-left:40px"><font color="#000080">mzero = Parser (cs -&gt; [])<br />
mzero &gt;&gt;= f = mzero</font></div><br />
mzero est l’élément absorbant de la composition des analyseurs, de même que 0 est l’élément absorbant de la multiplication des nombres réels.<br />
<br />
A ce stade, nous pouvons donner un premier exemple d’analyseur composé. Nous avons mis au point une primitive item qui renvoie le premier caractère d’un flux. Nous voudrions maintenant créer un analyseur qui renvoie le premier caractère s’il satisfait une certaine condition :<br />
<br />
<font color="#000080"><div style="margin-left:40px">sat :: (Char -&gt; Bool) -&gt; Parser Char<br />
sat f = item &gt;&gt;= \c -&gt; if f c then return c else mzero</div></font><br />
<br />
<b>La do-notation</b><br />
Une notation plus lisble, un sucre syntaxique est disponible dans Haskell pour manipuler les monades<br />
<br />
<div class="cms_table"><table width="500" class="cms_table"><tr valign="top" class="cms_table_tr"><TD class="cms_table_td"><b>Vanilla</b></TD>
<TD class="cms_table_td"><b>do-notation</b></TD>
</tr>
<tr valign="top" class="cms_table_tr"><TD class="cms_table_td">sat =</TD>
<TD class="cms_table_td">sat = do</TD>
</tr>
<tr valign="top" class="cms_table_tr"><TD class="cms_table_td">item &gt;&gt;= \c -&gt;</TD>
<TD class="cms_table_td">c &lt;- item</TD>
</tr>
<tr valign="top" class="cms_table_tr"><TD class="cms_table_td">if f c then…</TD>
<TD class="cms_table_td">if f c then…</TD>
</tr>
</table></div>
<br />
Si on veut l’utiliser sans sauter de lignes, on peut placer les instructions entre accolades et les séparer par des points-virgules. Nous utiliserons la do-notation à l’avenir, qui est souvent plus claire. Un exercice peut-être de les reconvertir en notation salée.<br />
<br />
Ainsi, en do-notation :<br />
<font color="#000080"><div style="margin-left:40px">sat = do { c &lt;- item ; if f c then return c else mzero }</div></font><br />
<div style="margin-left:40px"><font color="#4b0082">&gt; parse (sat isDigit) &quot;123&quot;</font><br />
<font color="#8B4513">[('1', &quot;23&quot;)]</font><br />
<font color="#4b0082"><br />
&gt; parse (sat isDigit) &quot;abc&quot;</font><br />
<font color="#8B4513">[]</font></div><br />
Pour extraire un caractère en particulier, nous pouvons créer :<div style="margin-left:40px"><font color="#000080">char :: Char -&gt; Parser Char<br />
char c = sat (c==)</font></div><br />
<b>Des analyseurs récursifs</b><br />
Nous sommes également en mesure de créer des analyseurs récursifs. Si l’on veut extraire une chaîne du flux, nous pouvons par exemple écrire :<div style="margin-left:40px"><font color="#000080">string :: String -&gt; Parser String<br />
string &quot;&quot; = return &quot;&quot;<br />
string (c:cs) = do { x &lt;- char c; xs &lt;- string cs; return (x : xs) }</font></div><br />
Cette récursion fonctionne de la même façon que la récursion fonctionnelle habituelle. Il y a un cas d’arrêt (string &quot;&quot;) et un cas n+1. Il est éclairant de noter que si, dans le cas n+1, la fonction 'char' échoue, elle renverra l’élément absorbant mzero et que donc la fonction 'string' sera égale à mzero :<br />
<div style="margin-left:40px"><font color="#4b0082">&gt; parse (string &quot;abc&quot;) &quot;abcdef&quot;</font><br />
<font color="#8B4513">[(&quot;abc&quot;, &quot;def&quot;)]</font><br />
<br />
<font color="#4b0082">&gt; parse (string &quot;abc&quot;) &quot;defghi&quot;</font><br />
<font color="#8B4513">[]</font></div><br />
<b>Enrichir encore la monade</b><br />
0 est l’élément absorbant de la multiplication, mais il est également l’élément neutre de l’addition : x + 0 = x<br />
En définissant une fonction d’addition pour nos analyseurs, nous pouvons également donner ce rôle à mzero :<br />
<div style="margin-left:40px"><font color="#000080">mplus :: Parser a -&gt; Parser a -&gt; Parser a<br />
mplus p q = Parser (\cs -&gt; (parse p cs) ++ (parse q cs))<br />
</font></div><i>N.B. (++) concatène deux listes</i><br />
<br />
mplus concatène les listes de résultats obtenus en appliquant p et q à cs. Or [] ++ [1,2,3] = [1,2,3] et [1,2,3] ++ [] = [1,2,3]. mzero, comme 0, est ainsi, selon la relation choisie, soit l’élément absorbant, soit l’élément neutre (au passage, nous venons de définir un monoïde, c’est-à-dire un ensemble pourvu d’une opération associative qui admet un élément neutre).<br />
<br />
Lorsque mzero et mplus sont définis pour un type, il peut être défini comme une instance de la catégorie <a href="http://en.wikibooks.org/wiki/Haskell/MonadPlus" target="_blank">MonadPlus</a>.<br />
<br />
Comme nous choisirons des exemples d’analyseurs simples et déterministes, mplus peut être restreint par un opérateur (+++) qui ne retient que le premier choix possible parmi ceux renvoyés par mplus :<div style="margin-left:40px"><font color="#000080"><br />
(+++) :: Parser a -&gt; Parser a -&gt; Parser a<br />
p +++ q = Parser (cs -&gt; case p `mplus` q of<br />
<div style="margin-left:40px">[] -&gt; []<br />
(r:_) -&gt; [r])</div></font></div><br />
<b>De l’intérêt d’être un monoïde.</b><br />
Grâce à ses fonctionnalités supplémentaires, notre type Parser peut désormais gérer l’échec. Tandis que mzero permet de le représenter, mplus et son dérivé (+++) permettent de le surmonter. <br />
 <br />
On peut ainsi définir deux constructeurs d’analyseurs many et many1, qui prennent en argument un analyseur plus primitif et qui le répètent respectivement un nombre indéfini de fois et au moins une fois :<br />
<br />
<font color="#000080"><div style="margin-left:40px">many :: Parser a -&gt; Parser [a]<br />
many p = many1 p +++ (return [])</div></font><br />
<i><br />
Attention, return [] est <u>différent </u>de mzero : return [] = Parser (cs -&gt; [([], cs)] tandis que mzero = Parser (cs -&gt; []).</i><br />
<br />
Ainsi, si many1 échoue, many retourne un résultat <u>positif</u> de 0 occurrences. En effet, mzero `mplus` (return []) = mzero +++ (return []) = return [] puisque mzero est l’élément neutre.<br />
<br />
<font color="#000080"><div style="margin-left:40px">many1 :: Parser a -&gt; Parser [a]<br />
many1 p = do {t &lt;- p; ts &lt;- many p; return (t:ts) }</div></font><br />
<div style="margin-left:40px"><font color="#4b0082">&gt; parse (many1 (char 'b')) &quot;bbc&quot;</font><br />
<font color="#8B4513">[(['b', 'b'], &quot;c&quot;)]</font><br />
&gt; parse (many (char 'b')) &quot;abbc&quot;<br />
<font color="#8B4513">[([], &quot;abbc&quot;)]</font><br />
&gt; parse (many1 (char 'b')) &quot;abbc&quot;<br />
<font color="#8B4513">[]</font></div><br />
<b>Un dernier pas avant Pascal…</b><br />
Sur le même modèle, nous définissons encore deux constructeurs d’analyseurs qui nous permettront de composer un analyseur d’expressions arithmétiques simples (notre <a href="http://fr.wikipedia.org/wiki/Pascaline" target="_blank">Pascaline </a>à nous).<br />
<br />
chainl et chainl1 prennent en argument deux analyseurs : un qui permet d’obtenir les termes de l’opération (Parser a), un autre qui permet d’obtenir l’opération elle-même (Parser (a -&gt; a -&gt; a)). Il reposent sur le même principe de co-récursion et de gestion d’échec par l’élément neutre et l’addition.<div style="margin-left:40px"><font color="#000080"><br />
chainl :: Parser a -&gt; Parser (a -&gt; a -&gt; a) -&gt; a -&gt; Parser a<br />
chainl p op a = chainl1 p op +++ return a <br />
chainl1 :: Parser a -&gt; Parser (a -&gt; a -&gt; a) -&gt; Parser a<br />
p `chainl1` op = do { a &lt;- p; rest a}<br />
    <div style="margin-left:40px">where rest a = do { f &lt;- op; b &lt;- p; rest (f a b) } +++ return a</div></font></div><br />
<b>L’interpréteur</b><br />
Maintenant vous en savez assez pour comprendre le code suivant. La beauté réside dans sa forme très proche d’une grammaire au format Backus-Naur (les déclarations de type sont placées en premier pour le faire ressortir ensuite):<br />
                        <br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td valign="top"><pre style="margin: 0">expr <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span style="color: #0080ff;">Int</span>
term <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span style="color: #0080ff;">Int</span>
factor <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span style="color: #0080ff;">Int</span>
digit <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span style="color: #0080ff;">Int</span>
symb <span style="color: #339933;">:</span><span style="color: #339933;">:</span> <span style="color: #0080ff;">String</span> -&gt; Parser <span style="color: #0080ff;">String</span>
addop <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span class="br0">&#40;</span><span style="color: #0080ff;">Int</span> -&gt; <span style="color: #0080ff;">Int</span> -&gt; <span style="color: #0080ff;">Int</span><span class="br0">&#41;</span>
mulop <span style="color: #339933;">:</span><span style="color: #339933;">:</span> Parser <span class="br0">&#40;</span><span style="color: #0080ff;">Int</span> -&gt; <span style="color: #0080ff;">Int</span> -&gt; <span style="color: #0080ff;">Int</span><span class="br0">&#41;</span>
&nbsp;
<span style="color: #808080;">-- BNF GRAMMAR</span>
&nbsp;
expr = term <span style="color: #339933;">`chainl1`</span> addop
term = factor <span style="color: #339933;">`chainl1`</span> mulop
factor = digit +++ <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> symb <span style="color: #FF0000;">&quot;(&quot;</span>; n &lt;- expr; symb <span style="color: #FF0000;">&quot;)&quot;</span>; <span style="color: #0000ff;">return</span> n <span class="br0">&#125;</span>
&nbsp;
symb cs = string cs
digit = <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> x &lt;- sat isDigit; <span style="color: #0000ff;">return</span> <span class="br0">&#40;</span>ord x - ord <span style="color: #FF0000;">'0'</span><span class="br0">&#41;</span> <span class="br0">&#125;</span>
&nbsp;
addop = <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> symb <span style="color: #FF0000;">&quot;+&quot;</span>; <span style="color: #0000ff;">return</span> <span class="br0">&#40;</span>+<span class="br0">&#41;</span> <span class="br0">&#125;</span> +++ <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> symb <span style="color: #FF0000;">&quot;-&quot;</span>; <span style="color: #0000ff;">return</span> <span class="br0">&#40;</span>-<span class="br0">&#41;</span> <span class="br0">&#125;</span>
mulop = <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> symb <span style="color: #FF0000;">&quot;*&quot;</span>; <span style="color: #0000ff;">return</span> <span class="br0">&#40;</span>*<span class="br0">&#41;</span> <span class="br0">&#125;</span> +++ <span style="color: #0000ff;">do</span> <span class="br0">&#123;</span> symb <span style="color: #FF0000;">&quot;/&quot;</span>; <span style="color: #0000ff;">return</span> <span style="color: #0080ff;">div</span> <span class="br0">&#125;</span></pre></td></tr></table></pre>
</div><br />
<div style="margin-left:40px"><font color="#4b0082">&gt; parse expr &quot;1+1-1*1&quot;</font><br />
<font color="#8B4513">[(1, &quot;&quot;)] </font> <font color="#2F4F4F">-- CQFD</font></div><br />
<b>Conclusion</b><br />
La courbe d’apprentissage des langages fonctionnels est assez raide. Ce billet utilise un certain nombre de concepts nouveaux et la tournure d’esprit est très différente de celle d’un langage impératif. Néanmoins, une fois surmonté cet obstacle, la modularité, l’extensibilité et la généralité des constructions devient évidente. Avec les primitives décrites ici on peut facilement écrire un interpréteur pour un autre langage, un lecteur de fichiers html ou csv, etc. Je joins un code source consolidé (au format txt à convertir en .hs, qui n’est pas autorisé comme PJ par le site :( ) pour ceux qui souhaiteraient expérimenter. <a href="https://www.developpez.net/forums/attachments/p177164d1431013077/autres-langages/perl/langage/langage-comparer-perl-d-autres-langages-c/parser-1.txt/"  title="Nom : parser-1.txt
Affichages : 116
Taille : 1,9 Ko">parser-1.txt</a><br />
<br />
Je profiterai de cette possibilité dans un des prochains billets pour présenter quelques concepts basiques de logique propositionnelle. Je compte également m’appesantir un peu sur les monoïdes et présenter quelques structures de données fonctionnelles intéressantes.<br />
<br />
<b>P.S : les questions, commentaires, suggestions, corrections, etc. sont les bienvenus!!</b><br />
<br />
<u>Exercices :</u><br />
- lire l’article d’origine<br />
- adapter le programme pour prendre en compte la possibilité d’espaces entre les chiffres (ex : &quot;1 + 1 – 1 * 1&quot;)<br />
- ajouter les nombres de plusieurs chiffres<br />
- ajouter la possibilité d’élever un nombre à une puissance n</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b490/analyse-syntaxique-monadique/</guid>
		</item>
		<item>
			<title>En quête de feedback...</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b473/quete-feedback/</link>
			<pubDate>Wed, 29 Apr 2015 07:18:37 GMT</pubDate>
			<description>Voici le quinzième billet de...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Voici le quinzième billet de ce blog et je suis saisi de doutes... Quelqu'un a-t-il des conseils sur la direction à prendre?<br />
<br />
<b>Quelques idées pour la suite</b><br />
Venant d'introduire les monades, je comptais par la suite en montrer quelques applications: comment représenter le hasard dans un contexte fonctionnel pur? comment effectuer élégamment l'analyse lexicale (le &quot;parsing&quot;) d'une chaîne de caractères? Je voulais introduire également d'autres catégories: foncteurs, endo-foncteurs, monoïdes et montrer leurs relations, structurelles ou fonctionnelles. Enfin, j'avais d'autres idées diverses: présentation de structures de données utiles en programmation fonctionnelle, traduction de certains papiers de recherche ou billets de blog...<br />
<br />
<b>Mes doutes et questions</b><br />
Est-ce que ces directions intéressent mes aimables lecteurs? Est-ce que d'autres leur paraissent plus pertinentes? Ont-ils des réserves sur la programmation fonctionnelle et demandent que soient éclaircis certains points? Au contraire, les billets sont-ils trop basiques ou trop fragmentés?<br />
<br />
<b>Conclusion</b><br />
Vous l'avez compris, je suis en quête de feedback. Merci d'avance à ceux qui voudront bien laisser quelques conseils en commentaire!</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b473/quete-feedback/</guid>
		</item>
		<item>
			<title>Jack Bauer et les monades</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b467/jack-bauer-monades/</link>
			<pubDate>Mon, 27 Apr 2015 12:44:13 GMT</pubDate>
			<description>Attention, ceci n’est pas la...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Attention, ceci n’est pas la saison 8 de 24H. Vous y trouverez en revanche une réflexion palpitante sur les modèles de gestion des exceptions à disposition du programmeur, dans l’esprit du <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b459/quoi-peuvent-bien-servir-monades/" target="_blank">billet précédent</a>. Je commencerai par le résumer en quelques phrases car je crains que l’aridité du titre et la longueur du développement n’en aient découragé certains. J’en recommande néanmoins la lecture, pour son intérêt intrinsèque et le dur travail que m’a demandé sa traduction.<br />
<br />
<b>Précédemment, dans func’programming…</b><br />
La théorie des catégories permet de formuler, de façon consciente et explicite, les concepts, restrictions et à-peu-près qui façonnent un langage de programmation. En effet, les « fonctions » qui y sont utilisées, loin d’être de simples fonctions mathématiques, y sont des outils de puissance variable qui autorisent –ou non, et de façons différentes- la gestion des erreurs, l’interaction avec le monde extérieur ou la référence à des paramètres globaux : elles sont donc mieux décrites comme composant des « catégories », au sens de la théorie des catégories.<br />
<br />
Les flèches de Kleisli sont l’abstraction de la théorie des catégories la plus immédiatement applicable aux langages de programmation : alors qu’une fonction mathématique associe un ensemble A à un ensemble B, la flèche de Kleisli associe un ensemble A à un ensemble K(B), où K ajoute un supplément d’âme à B : la gestion des erreurs, l’interaction avec le monde extérieur, etc. Reste à définir un nouvel opérateur de composition, car, si a -&gt; b et b -&gt; c se composent naturellement, a -&gt; K(b) et b -&gt; K(c) ne le peuvent pas.<br />
<br />
Les catégories utilisées dans la plupart des langages de programmation sont fixées par le standard et ne peuvent pas être modifiées, obligeant à respecter des conventions parfois à la limite de la lisibilité. Un langage comme Haskell, qui permet de manipuler les abstractions de la théorie des catégories, lève cette restriction et permet de créer les catégories les plus adaptées à la tâche envisagée.<br />
<br />
<b>Jack Bauer</b><br />
Jack Bauer doit retrouver les codes qui empêcheront une bombe nucléaire d’exposer au cœur de Los Angeles. Pour cela, il doit mettre la main sur un mercenaire immoral, qui lui donnera l’adresse d’un terroriste, qui détient quelque part une pirate informatique très jolie, qui a caché dans la consigne d’une gare les codes en question. A première vue :<br />
<br />
<div style="margin-left:40px"><font color="#000080">if ( prendreCodes(cachetteDeLaPirate(adresseDuTerroriste(repaireDuMercenaire()))) == CODE ) print « Ouf !» else print « Boom ! »</font></div><br />
Néanmoins, il y a une bonne chance que Jack Bauer, à cause de sa fille, échoue dans l’une de ces tâches… <br />
<br />
<b>MacGyver : scotch, ficelle et boîte d’allumettes</b><br />
C’est l’option du langage C. On n’a pas d’outil spécial pour la gestion des exceptions, alors on fait avec ce qu’on a sous la main. Première solution : renvoyer une valeur conventionnelle pour l’échec, comme NULL, -1, etc. Je ne m’étendrai pas trop sur les défauts de cette méthode, je pense que tout le monde est au courant. Seconde solution : la valeur de retour est un boléen qui indique s’il y a échec, et le véritable retour est placé dans un paramètre à modifier :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">char</span>* repaire ;
<span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>repaireDuMercenaire<span style="color: black;">&#40;</span>repaire<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">char</span>* adresse ;
  <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>adresseDuTerroriste<span style="color: black;">&#40;</span>repaire, adresse<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
     <span style="color: #0000ff;">char</span>* cachette ;
     <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>cachetteDeLaPirate<span style="color: black;">&#40;</span>adresse, cachette<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
        <span style="color: #0000ff;">long</span> codes ;
        <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>prendreCodes<span style="color: black;">&#40;</span>cachette, &amp;codes<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
           <span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span>codes == CODES<span style="color: black;">&#41;</span> printf<span style="color: black;">&#40;</span>&laquo; OUF ! &raquo;<span style="color: black;">&#41;</span> ; <span style="color: #0000ff;">else</span> printf<span style="color: black;">&#40;</span>&laquo; Boom &raquo;<span style="color: black;">&#41;</span> ;
        <span style="color: black;">&#125;</span>
        <span style="color: #0000ff;">else</span> printf <span style="color: black;">&#40;</span>&laquo; Boom ! &raquo;<span style="color: black;">&#41;</span> ;
      <span style="color: black;">&#125;</span>
      <span style="color: #0000ff;">else</span> printf <span style="color: black;">&#40;</span>&laquo; Boom ! &raquo;<span style="color: black;">&#41;</span> ;
    <span style="color: black;">&#125;</span>
     <span style="color: #0000ff;">else</span> printf <span style="color: black;">&#40;</span>&laquo; Boom ! &raquo;<span style="color: black;">&#41;</span> ;
  <span style="color: black;">&#125;</span>
   <span style="color: #0000ff;">else</span> printf <span style="color: black;">&#40;</span>&laquo; Boom ! &raquo;<span style="color: black;">&#41;</span> ;
<span style="color: black;">&#125;</span>
<span style="color: #0000ff;">else</span> printf <span style="color: black;">&#40;</span>&laquo; Boom ! &raquo;<span style="color: black;">&#41;</span> ; <span style="color: #808080;">// ce else est en trop, mais il faut compter pour s'en apercevoir...</span></pre></td></tr></table></pre>
</div><br />
C’est une solution qui marche, mais… Disons que de temps en temps, plutôt que d’improviser une montgolfière miniature, on préférerait prendre l’ascenseur.<br />
<br />
<b>James Bond : une voiture qui vole</b><br />
La deuxième solution est d’ajouter des features au langage. On a une voiture, on voudrait voler, on achète une voiture qui vole. On a des fonctions C, on voudrait gérer les exceptions de façon structurée, on passe à C++. Chacune des fonctions contient un garde-fou : si l’action échoue, on balance une exception et on court-circuite l’exécution du programme jusqu’à la clause de gestion des exceptions (le catch).<br />
<br />
Cette solution est largement préférable à la première. La solution évidente :<br />
<br />
<div style="margin-left:40px"><font color="#000080">if ( prendreCodes(cachetteDeLaPirate(adresseDuTerroriste(repaireDuMercenaire()))) == CODE )</font></div><br />
n’est toujours pas possible, puisqu’il faut au moins un try quelque part. Mais un code comme :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C++ :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">try</span> <span style="color: black;">&#123;</span>
  <span style="color: #0000ff;">long</span> codes = prendreCodes<span style="color: black;">&#40;</span>cachetteDeLaPirate<span style="color: black;">&#40;</span>adresseDuTerroriste<span style="color: black;">&#40;</span>repaireDuMercenaire<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
  std : :cout &lt;&lt; <span style="color: black;">&#40;</span>codes == CODE ? &laquo; ouf ! &raquo; : &laquo; boom ! &raquo;<span style="color: black;">&#41;</span> ;
<span style="color: black;">&#125;</span>
<span style="color: #0000ff;">catch</span> <span style="color: black;">&#40;</span>&#133;<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
  std : :cout &lt;&lt; &laquo; Aaargh ! Kim Bauer a encore frapp&eacute; ! &raquo;
<span style="color: black;">&#125;</span></pre></td></tr></table></pre>
</div><br />
est tout de même lisible et robuste.<br />
<br />
L’inconvénient est qu’il a fallu réécrire tout le programme en C++ et que, entre temps, Los Angeles est devenue un ville fantôme. Plus sérieusement, la gestion des exceptions a son côté obscur (voir <a href="http://www.joelonsoftware.com/items/2003/10/13.html" target="_blank">ce billet</a>, par exemple) et ses difficultés (voir<a href="http://www.gotw.ca/gotw/008.htm" target="_blank"> celui-là</a>). C’est un de ces domaines où l’expérience ne s’achète pas.<br />
<br />
<b>Haskell : la monade des « peut-être »</b><br />
Comme j’introduis la première fois, sous l’angle technique, l’utilisation de la théorie des catégories, j’utiliserai la façon la plus simple de gérer les exceptions –mais un mécanisme plus puissant demanderait des modifications mineures.<br />
<br />
Adoptons l’approche développée dans le billet précédent : la première étape est de décrire ce que serait le domaine d’arrivée d’une fonction susceptible d’échec ; la réponse est qu’il s’agit de l’ensemble des éléments de succès et, a minima (c’est notre approche aujourd’hui), d’un élément d’échec. Créons un type pour représenter ce domaine d’arrivée :<br />
<div style="margin-left:40px"><br />
<font color="#000080">data PeutEtre a = Juste a | Rien </font> <font color="#8B4513">--à noter qu’un constructeur de type, pas une valeur, représente l’éche</font>c</div><br />
par exemple, la fonction retournant l’adresse du terroriste aurait pour type :<br />
<br />
<div style="margin-left:40px"><font color="#000080">adresseDuTerroriste :: String -&gt; PeutEtre String</font></div><br />
Elle prend en argument le repaire du mercenaire, et si Jack torture suffisamment cruellement le mercenaire, elle retourne Juste adresse, sinon Rien. Néanmoins, la fonction cachetteDeLaPirate a également pour type :<br />
<br />
<font color="#000080"><div style="margin-left:40px">cachetteDeLaPirate :: String -&gt; PeutEtre String</div></font><br />
<br />
On ne peut donc pas lui donner directement la valeur de retour de adresseDuTerroriste… <br />
<br />
<b>Composition et identité</b><br />
Vient la deuxième étape décrite dans le billet précédent : indiquer comment se font la composition et l’identité :<br />
<br />
<font color="#000080"><div style="margin-left:40px">compose : : PeutEtre a -&gt; (a -&gt; PeutEtre b) -&gt; PeutEtre b</div></font><br />
<br />
Rien de trop compliqué : si on reçoit Rien, le résultat est forcément Rien ; si on reçoit Juste x, alors le résultat est f x :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">compose Rien <span style="color: #0000ff;">_</span> = Rien
compose <span class="br0">&#40;</span>Juste x<span class="br0">&#41;</span> f = f x</pre></td></tr></table></pre>
</div><br />
Voilà pour la composition. L’identité est la fonction qui associe à x dans A, x dans K(A). Elle est encore plus simple à définir.<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">identity <span style="color: #339933;">:</span> <span style="color: #339933;">:</span> a -&gt; PeutEtre a
identity x = Juste x</pre></td></tr></table></pre>
</div><br />
En effet, Rien n’existe pas dans A ; donc tout élément de A est nécessairement un succès, un Juste x.<br />
<br />
<b>Monade, classe et syntaxe</b><br />
Les monades sont en Haskell une classe de type, ou typeclass (pour un rappel de ces notions, voir <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b457/petite-cuillere-types-haskell/" target="_blank">ce billet</a>). Elle est définie de la façon suivante :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0">class Monad m where
  return :: a -&gt; m a <font color="#8B4513">--identity</font>
  (&gt;&gt;=) : : m a -&gt; (a -&gt; m b) -&gt; m b  <font color="#8B4513">--compose</font></pre></td></tr></table></pre>
</div><br />
Notre monade PeutEtre pourrait donc être définie de la façon suivante :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #0000ff;">instance</span> <span style="color: #0080ff;">Monad</span> PeutEtre <span style="color: #0000ff;">where</span>
	<span style="color: #0000ff;">return</span> = identity
	<span class="br0">&#40;</span>&gt;&gt;=<span class="br0">&#41;</span> = compose</pre></td></tr></table></pre>
</div><br />
Nous pouvons donc exprimer la quête de Jack de la façon suivante :<br />
<div style="margin-left:40px"><br />
<font color="#000080">repaireDuMercenaire &gt;&gt;=  adresseDuTerroriste &gt;&gt;= cachetteDeLaPirate …</font><font color="#8B4513"> --oh shit !</font></div><br />
C’est un bon début, mais il reste à comparer les codes obtenus avec les codes corrects. Or le comparateur (==) prend deux arguments de type identique, et j’ai un PeutEtre Code d’un côté, un Code de l’autre. Il faut donc que je puisse lier le code contenu dans le PeutEtre à une variable, afin de le comparer au code correct. Nouvel essai :<br />
<br />
<div style="margin-left:40px"><font color="#000080">repaireDuMercenaire &gt;&gt;=  adresseDuTerroriste &gt;&gt;= cachetteDeLaPirate &gt;&gt;= \code -&gt; …</font><font color="#8B4513"> --oh shit !</font></div><br />
Mais il faut encore que le résultat de la comparaison soit un PeutEtre, si je veux respecter le type de compose… Heureusement nous avons identity pour cela, ouf !<br />
<br />
<div style="margin-left:40px"><font color="#000080">repaireDuMercenaire &gt;&gt;=  adresseDuTerroriste &gt;&gt;= cachetteDeLaPirate &gt;&gt;= \code -&gt; return (code == CODE)</font></div><br />
Pour récapituler :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:96px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br /></div></td><td valign="top"><pre style="margin: 0">queSestIlPasse <span class="br0">&#40;</span>Juste <span style="color: #339933;">True</span><span class="br0">&#41;</span> = &laquo; Ouf ! &raquo;
queSestIlPasse <span class="br0">&#40;</span>Juste <span style="color: #339933;">False</span><span class="br0">&#41;</span> = &laquo; Boom ! &raquo;
queSestIlPasse Rien = &laquo; Aaargh ! Kim Bauer a encore frapp&eacute; &raquo;
&nbsp;
resumeDeLEpisode = printLn $ queSestIlPasse <span class="br0">&#40;</span>repaireDuMercenaire &gt;&gt;=  adresseDuTerroriste &gt;&gt;= cachetteDeLaPirate &gt;&gt;= <span style="color: #339933;">\code</span> -&gt; <span style="color: #0000ff;">return</span> <span class="br0">&#40;</span>code == CODE<span class="br0">&#41;</span><span class="br0">&#41;</span></pre></td></tr></table></pre>
</div><br />
<b>Conclusion :</b><br />
Voici une première monade, toute simple. Elle existe déjà dans la bibliothèque standard d’Haskell, sous le nom de Maybe. L’important, plus que l’implémentation (on l’a vu, rien de très difficile), est d’avoir pu créer un système de gestion des exceptions en quelques lignes de code, <i>sans support spécifique du langage</i>.<br />
<br />
<u>Exercices :</u><br />
<div style="margin-left:40px">- définir une nouvelle monade, pour un type :<br />
data Try a = Succes a | Exception String<br />
 qui renvoie soit Succes a si tout s’est bien passé, soit Exception messageDErreur<br />
- une liste est-elle une monade ? Justifiez<br />
- si vous ne l’avez pas déjà fait, lisez le billet précédent</div></blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b467/jack-bauer-monades/</guid>
		</item>
		<item>
			<title>A quoi peuvent bien servir les monades?</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b459/quoi-peuvent-bien-servir-monades/</link>
			<pubDate>Fri, 24 Apr 2015 09:38:27 GMT</pubDate>
			<description>Ce billet est la traduction...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Ce billet est la traduction d'<a href="https://cdsmith.wordpress.com/2012/04/18/why-do-monads-matter/" target="_blank">un billet écrit en anglais</a> par cdsmith: <i>Why do Monads Matter?</i><br />
<br />
<b>Théorie des catégories et programmation : correspondance parfaite ou distraction abstraite ?</b><br />
Si vous êtes un développeur : avez-vous entendu parler des monades ? Vous êtes-vous demandé de quoi il s’agissait ? Avez-vous tenté d’apprendre Haskell et lutté avec cette notion ? Avez-vous regardé des vidéos de « Channel 9 » où un tas de chercheurs de Microsoft en parlent, mais ont du mal à faire le lien avec votre expérience de tous les jours ?<br />
<br />
Si vous êtes un mathématicien : avez-vous entendu parler l’intérêt de la théorie des catégories pour la science informatique ? Avez-vous cherché un énoncé clair des raisons de cet intérêt ? Douté qu’il y en ait de satisfaisantes ? Peut-être que, comme un de mes amis qui m’a interrogé à ce sujet il y a un an, vous vous rappelez avoir entendu que la théorie des catégories avait suscité beaucoup d’excitation dans la recherche informatique des années 90, mais n’avez jamais su si c’était une impasse ou si cela avait débouché ?<br />
<br />
C’est le genre de questions auxquelles je vais répondre. Mon but est de démontrer, précisions et exemples à l’appui:<br />
- d’où viennent les idées et intuitions relatives à la théorie des catégories en informatique<br />
- pourquoi le futur de la programmation repose sur ces idées, et pourquoi les ignorer, comme le font les langages les plus répandus, est dommageable<br />
- quel est l’état de l’art dans l’application de la théorie des catégories aux problèmes posés par l’informatique<br />
<br />
Si vous vous lancez dans cet article sans aucune notion sur la théorie des catégories, n’ayez pas peur ! C’est une des introductions les plus progressives aux catégories et aux monades que vous pourrez trouver. Cependant, lisez lentement pour bien comprendre la définition des catégories et des idées connexes, comme la composition de fonction, qui sont cruciales. Par ailleurs, vous pouvez passez outre ou rapidement sur la section « Quel est le rapport avec les monades ? » où j’explique en quoi le sujet correspond au sens donné traditionnellement aux monades par les mathématiques : il n’est pas vraiment nécessaire de le savoir.<br />
<br />
En revanche, si vous êtes mathématicien, vous pouvez laisser tomber les rappels basiques sur la théorie des catégories et approfondir plutôt les passages où j’adopte la perspective des sciences informatiques. Attention tout de même, j’introduis les monades via les catégories de Kleisli, donc prenez le temps de vous assurer  que vous êtes familiers de la relation entre les deux.<br />
<br />
Prêts ? Allons-y !<br />
<b><br />
Programmation informatique et fonctions : une relation ténue.</b><br />
Question rapide : les programmeurs utilisent-ils des fonctions ?<br />
Demandez à n’importe quel programmeur, il vous répondra : OUI ! Les fonctions sont un des outils les plus basiques de la programmation. Vous aurez droit à un regard atterré : bien sûr ! Autant demander si les charpentiers utilisent des clous, non ?<br />
<br />
La vérité est pourtant un peu plus compliquée. Pour un mathématicien, une fonction associe simplement des valeurs d’entrée et des valeurs de sortie… et c’est tout ! Deux fonctions qui associent les mêmes valeurs d’entrée aux mêmes valeurs de sortie sont identiques. Vous pouvez représenter des fonctions par des formules, mais aussi, simplement, par des tables d’entrée/sortie, ou par des graphes s’il s’agit de nombres réels. Si vous demandez des exemples de fonctions à des programmeurs, pourtant, ils seront bien différents : pour les appeler des fonctions, il faudrait avoir raté le cours d’analyse. Ce sont des choses que les programmeurs appellent sans arrière-pensée des fonctions, mais qui n’en sont pas du tout pour les mathématiciens :<br />
- des « fonctions » qui renvoient des nombres choisis au hasard… et donneront des résultats différents à chaque appel ;<br />
- des « fonctions » qui renvoient une réponse le dimanche, mais une autre le lundi, encore une autre le mardi, et ainsi de suite ;<br />
- des « fonctions » qui font apparaître des mots sur l’écran d’un ordinateur à chaque calcul d’une valeur.<br />
<br />
Qu’est-ce que cela peut bien vouloir dire ? La plupart des programmeurs vivent paisiblement en appelant ces choses des fonctions, alors qu’elles en sont bien différentes. Mais une seconde ! Ces deux notions ont quand même beaucoup en commun. En particulier : (a) des paramètres, qui représentent leur domaine de départ et (b) des valeurs de retour, qui représentent leur domaine d’arrivée (bien des programmeurs parlent de fonction qui n’ont ni argument, ni valeur de retour… mais pas la peine d’être pointilleux, il suffit de regarder leurs domaines de départ et d’arrivée comme des ensembles à un élément, et on sauve les apparences)<br />
<br />
Encore plus important, ces « fonctions » ont en commun avec les fonctions mathématiques d’être constamment composées, en prenant en paramètre le résultat d’une autre fonction.  Quand je parle de composition, c’est presqu’exactement au sens mathématique : (f . g) (x) = f(g(x)). D’ailleurs, la raison pour laquelle les fonctions existent est pour pouvoir les composer entre elles ! Autrefois, dans les premiers temps de l’informatique, on se satisfaisait de garder trace d’une information en la stockant à un endroit déterminé de la mémoire; mais devoir garder trace de tous ces emplacements compliquait l’écriture séparée des parties d’un programme pour les assembler ensuite ; on est donc passé à l’utilisation de fonctions et de leur composition.<br />
<br />
Récapitulons une première fois :<br />
<br />
Quand les programmeurs parlent de fonctions, il s’agit d’un concept différent de celui des mathématiciens.<br />
Ce qu’ils entendent c’est : des valeurs d’entrée (le domaine d’entrée) et des valeurs de retour (domaine d’arrivée), et encore plus important, la composition de ces objets.<br />
<br />
<b>Puis vint la théorie des catégories…</b><br />
Nous avons conclu la section précédente les mains pleines de choses qui ressemblent à des fonctions, qui ont des domaines d’entrée et d’arrivée, et peuvent être composées. Pour autant, elles diffèrent des fonctions au sens mathématique. Déconcertant ? Non, pas vraiment. Les mathématiciens connaissent bien cela : ils ont un nom pour les systèmes d’objets « fonctionnesques » de ce genre, et ce nom est … roulement de tambours… CATEGORIES !<br />
<br />
Dans le jargon mathématique, les catégories sont :<br />
- des collections « d’objets » (pensez à des ensembles),<br />
- et de « flèches » (pensez à des fonctions entre les ensembles)<br />
- où chaque flèche a une domaine d’entrée et de sortie<br />
- chaque objet a une flèche « identité » (pensez à la fonction d’identité f(x) = x<br />
- et où les flèches peuvent être composées quand les domaines d’arrivée et d’entrée correspondent<br />
<br />
Avant d’accepter quelque chose comme une catégorie, il faut ajouter quelques règles : une fonction composée avec la fonction d’identité ne change pas, et la composition de fonctions obéit à la loi d’associativité. Cela n’est en soi pas surprenant, donc si cela devait vous paraître étrange, prenez un stylo, travaillez la définition de la composition de fonctions (  (f . g) (x) = f(g(x)) ) et simplifiez.<br />
<br />
Voilà l’intérêt des catégories : ce n’est pas une abstraction inventée sans but par une bande de mathématiciens. Les catégories sont définies de cette façon parce qu’on a remarqué que des centaines d’objets ressemblaient à des fonctions, avec des domaines d’entrée et de sortie et des compositions. Des objets de l’algèbre, comme les groupes, les anneaux et les espaces vectoriels ; des objets de l’analyse, comme les espaces métriques et topologiques ; des objets de l’analyse combinatoire, comme les éléments d’ensembles partiellement ordonnés ou les chemins des graphes ; des objets de la logique formelle, comme les preuves et les propositions. Ces objets peuvent presque tous être décrits avec les idées que nous venons de voir ! En bref, les catégories sont l’intuition juste pour traiter des objets qui ont un domaine d’entrée et de sortie et se composent. Et c’est justement ce que nous cherchons à faire.<br />
<b><br />
Les quatre cavaliers de la Catapocalypse</b><br />
Vous voyez maintenant ce que viennent faire ici les catégories : elles sont l’intuition juste de choses qui ne sont peut-être pas des fonctions, mais peuvent être composées comme des fonctions. Plus encore, ces idées de la théorie des catégorie répondent à des problèmes rencontrés par les développeurs informatiques.<br />
<br />
Il est temps d’être plus spécifique et d’introduire les quatre exemples qui nous guideront dans notre exploration. Chaque exemple illustre une façon dont les « fonctions » utilisées par les programmeurs diffèrent des fonctions des mathématiciens. Ces exemples reprennent des problèmes réels rencontrés par les programmeurs. Nous reviendrons sur l’aspect pratique de la question mais, pour l’instant, prenons le temps de nous familiariser avec chacun d’entre eux .<br />
<br />
<b>Le premier cavalier : l’échec.</b><br />
Le premier problème est celui de l’échec. Un programmeur tente souvent des choses qui peuvent échouer. Lire un fichier (il peut ne pas exister, on peut être privés des droits d’accès), communiquer par Internet (le réseau peut être coupé ou trop lent), et même faire de bons vieux calculs avec une grande quantité de données (on peut manquer de mémoire). En conséquence, l’échec est un souci constant.<br />
<br />
En général, les outils modernes de programmation informatique comptent sur la possibilité d’échec d’une fonction. Vous pouvez disposer d’une valeur de retour, mais vous pouvez aussi recevoir une raison pour l’échec de la tâche demandée. Quand cela se produit, le programmeur est responsable de la réponse à apporter : avertir quelqu’un, nettoyer la mémoire de l’ordinateur, ou parfois remettre les choses en état pour continuer. La façon dont ils permettent de traiter la possibilité constante d’un échec est un élément majeur dans le choix des techniques et des outils de développement.<br />
<br />
<b>Le deuxième cavalier : la dépendance</b><br />
Le deuxième cavalier est la dépendance à des informations extérieures. Tandis que les fonctions des mathématiciens ne dépendent que de leurs arguments, les programmeurs n’ont pas ce luxe. Les applications sont souvent un cauchemar de paramètres de configuration. Même les téléphones mobiles les plus simples ont des pages et des pages de paramètres : quelle est la langue de l’utilisateur ? la fréquence de sauvegarde ? faut-il crypter les communications ? Rares sont les applications qui n’ont pas un menu « paramètres » ou « préférences ». Dans bien d’autres contextes, les programmes dépendent d’informations qui sont un genre de « savoir partagé » de l’application ou d’une partie de celle-ci.<br />
<br />
La façon de traiter la question a évolué à travers les âges. Quand l’information était de toute façon rangée dans des régions bien connues de la mémoire, il était assez facile de la retrouver ; mais cela conduisait à d’autres problèmes quand différentes parties du programme devaient stocker différentes informations et pouvaient se marcher sur les pieds. La technique extrêmement influente de la programmation orientée objet peut être vue, au moins partiellement, comme une tentative de résolution du problème, par le regroupement de fonctions dans un objet contenant le contexte dont elles dépendent… mais quand les dépendances sont nombreuses, passer tout ces paramètres n’a plus rien de pratique.<br />
<br />
<b>Le troisième cavalier : l’incertitude</b><br />
Le troisième problème est l’incertitude, ou encore non-déterminisme. Une fonction normale associe une valeur d’entrée et une valeur de sortie. Une fonction non-déterministe associe une valeur d’entrée à un certain nombre de valeurs de sorties possibles. Le non-déterminisme est moins bien connu que les deux premiers problèmes, mais peut-être parce qu’il n’a pas encore été résolu par un langage généraliste !<br />
<br />
En effet, si la science informatique théorique traite abondamment du sujet, parce que c’est l’approche adaptée à un grand nombre de problèmes tels que l’analyse syntaxique, la vérification ou simplement les recherches, il n’a pas encore fait son chemin dans la pratique informatique.<br />
Le non déterminisme apparaît lorsque de multiples réponses peuvent correspondre à une requête ou une recherche, c’est-à-dire précisément là où le programmeur finit par dépendre d’outils extérieurs comme SQL, Prolog ou, plus récemment, LINQ et d’autres technologies intégrées au langage.<br />
Et lorsqu’une tâche ne paraît pas justifier l’utilisation d’un outil calibré pour le requêtage intensif ou les recherches, on finit par écrire ses propres boucles imbriquées et des structures de contrôles pour parcourir tous les possibles. Ce genre de situation est responsable de structures de code parmi les plus complexes qu’on puisse rencontrer aujourd’hui.<br />
<br />
Tandis que les deux premiers problèmes, échec et dépendance, ont été en partie résolus par des langages couramment utilisés, ce problème est traité principalement au moyen de sous-langages spécialisés, à la notable exception de LINQ.<br />
<b><br />
Le quatrième cavalier : destruction.</b><br />
Le quatrième problème est la destruction. Evaluer une fonction mathématique a pour seul effet d’obtenir la valeur retour. Mais dans un programme, les fonctions peuvent avoir un effet permanent sur le reste du monde : afficher une information, attendre les réponses d’autres ordinateurs, imprimer des documents et même envoyer des missiles, pour des systèmes militaires ! En conséquence, des points qui n’ont pas besoin d’être précisés en mathématiques, comme l’ordre d’évaluation, restent très importants.<br />
<br />
La nature destructrice (par quoi j’entends : qui a des effets irréversibles) des programmes informatiques a de nombreuses conséquences. Elle augmente le nombre d’erreurs commises. Elle rend plus difficile de diviser une tâche et de l’accomplir simultanément en plusieurs endroits, comme on pourrait vouloir le faire sur un ordinateur multi-cœur moderne, parce que l’ordre dans lequel on le fait peut être incorrect. Mais d’un autre côté, la destruction est justement l’intérêt premier de la programmation : une application qui n’aurait pas d’effet observable ne vaudrait pas la peine d’être lancée ! Donc nos fonctions doivent affronter la question de la destruction, dans tous les langages couramment utilisés.<br />
<br />
<b>Retour aux fonctions</b><br />
Nous avons inspecté des problèmes trouvés dans le monde informatique : le développement de logiciels qui peuvent échouer, doivent gérér plein de paramètres contextuels, modélisent des choix non-déterministes et ont parfois des effets sur les monde qui contraignent l’ordre des calculs.<br />
<br />
Il peut sembler que nous sommes très loin du monde simple et gentil des mathématiques. Et pourtant ! En y regardant de plus près –et en louchant suffisamment- chacune de ces quasi-fonctions peut être reconnue, après tout, comme une bonne petite fonction bien élevée. Il y a un coût, néanmoins : pour les transformer en de véritables fonctions nous devons modifier leur domaine d’arrivée. Voici comment cela fonctionne pour chacun des cavaliers.<br />
<br />
<b>Fonctions et échec</b><br />
Notre premier exemple était les fonctions qui peuvent échouer. Il n’est pas si difficile de voir qu’une fonction susceptible d’échec est en fait une fonction dont le résultat inclut :<br />
- des succès, qui sont les résultats possibles et attendus<br />
- des échecs, qui décrivent la raison de l’échec<br />
<br />
Donc pour tout ensemble A, nous pouvons définir un nouvel ensemble Err(A) qui contient A et les différentes raisons pour lesquelles l’échec est possible. Une fonction susceptible d’échec avec un domaine d’entrée A et un domaine d’arrivée B est en fait une fonction de A vers Err(B).<br />
<br />
<b>Fonctions et dépendance</b><br />
Nos deuxièmes pseudo-fonctions sont celles qui dépendent d’informations extérieures, comme les paramètres d’une application. Nous utilisons ici une astuce similaire : pour un ensemble A, nous définissons un ensemble Param(A) qui est l’ensemble des  fonctions partant des paramètres de l’application pour parvenir à l’ensemble A. Dès lors, une fonction « à contexte » de A à B est une fonction ordinaire de A à Param(B). Autrement dit, vous lui donnez une valeur issue de A et elle vous rend une fonction qui lie les paramètres de l’application à l’ensemble B.<br />
<br />
Aussi déconcertant que cela puisse paraître, une fonction dont le domaine d’arrivée est une autre fonction est simplement une fonction à deux arguments, à l’exception du fait qu’elle prend ses arguments un à la fois ! Prenez une minute pour vous en convaincre. La conversion entre ces deux idées équivalentes est parfois appelée « currying ».  Donc en modifiant le domaine d’arrivée de cette fonction, nous lui avons en fait adjoint un nouveau paramètre : les préférences enregistrées par l’application. Bien que ce ne soit pas très pratique (nous couvrirons cet aspect plus tard) , c’est exactement ce que nous souhaitions.<br />
<br />
<b>Fonctions et incertitude</b><br />
C’est peut-être l’exemple le plus facile à résoudre. Notre troisième type recouvrait les fonctions qui représentent le non-déterminisme : au lieu d’une réponse déterminée, elles renvoient toutes celles qui sont possibles.  Il suffit donc de définir, pour tout ensemble A, P(A) comme l’ensemble des sous-ensembles de A. Une fonction non-déterministe de A à B est une fonction ordinaire de A à P(B).<br />
<br />
<b>Fonctions et destruction</b><br />
Nous devons enfin traiter les fonctions qui ont un effet destructeur. Notre réponse sera un peu plus élaborée : pour tout ensemble A, nous définissions IO(A) (pour input/output, qui correspond à la notion d’effets résultant de l’interaction avec le reste du monde). Un élément de l’ensemble IO(A) est une liste d’instructions pour obtenir un membre de A : ce n’est donc pas un membre de A, mais plutôt la façon d’en obtenir un, et cette procédure peut avoir un nombre quelconque d’effets observables.<br />
<br />
Nous utilisons la même astuce et modifions le domaine d’arrivée : une fonction destructrice de A à B est une fonction mathématique ordinaire de A à IO(B). Autrement dit, si vous me donnez un A, comme je suis une simple fonction mathématique, je ne peux pas parcourir les étapes jusqu’à B mais je peux vous dire lesquelles il faut respecter.<br />
<br />
Mais qu’en est-il de la composition ? C’est merveilleux d’être de retour dans le monde simple des fonctions, mais vous souvenez-vous de ce qui nous y a conduit ? Nous voulions des fonctions parce que nous voulions pouvoir les composer, mais il semble que ce ne soit plus possible ! J’avais des fonctions, susceptibles d’échec, de A à B et de B à C, je les ai échangées contre des fonctions de A à Err(B) et de B à Err(C) et les domaines ne correspondent plus, je ne peux plus les composer !<br />
<br />
Oh non !<br />
<br />
<b>Retenez vos montures, Heinrich Kleisli arrive à la rescousse</b><br />
Bon, tout n’est pas perdu, c’est juste que je n’ai pas dit encore comment composer ces fonctions « spéciales ».<br />
<br />
Parce qu’un matheux a découvert ces choses avant nous, on les appelle par son nom : ce sont les flèches de Kleisli. Faites attention parce qu’il y a deux choses à garder à l’esprit : premièrement, les flèches de Kleisli sont de bonnes vieilles fonctions, on peut les composer comme des fonctions et c’est parfait; mais en même temps elles sont « spéciales » et on peut également les composer comme des flèches de Kleisli.<br />
<br />
Vous vous souvenez de ce qu’on avait dit ? La bonne façon de penser la composition est de penser par catégorie. Les ensembles sont une catégorie, et c’est celle qu’utilise les fonctions mathématiques. Mais nous voulons un autre genre de catégorie désormais, appelée catégorie de Kleisli. Si vous avez oublié la définition des catégories, c’est le moment de la revoir. Pour définir une catégorie, on a besoin d’objets, de flèches, d’identités et de composition.<br />
<br />
Pour rester simple, les objets de la nouvelle catégorie seront les mêmes : ce sont juste des ensembles de choses.<br />
Les flèches, sans surprise, sont des flèches de Kleisli. <br />
<br />
Je ne vous ai pas encore dit à quoi ressemblent les identités et la composition, alors voici :<br />
<br />
Commençons par l’échec : soient une flèche de Kleisli « échec » de A à B, et une de B à C. Nous cherchons à les composer en une flèche Kleisli de A à C. En d’autres mots, nous avons une fonction ordinaire de A à Err(B) et une autre de B à Err(C) et nous en cherchons une qui va de A à Err(C). Prenez une minute pour y réfléchir.<br />
<br />
L’idée centrale de la gestion des erreurs est que si la première fonction donne une erreur, il faut s'arrêter et déclarer l’erreur. C’est seulement si la première fonction réussi qu’il faut passer à la seconde, et donner le résultat (qu’il s’agisse d’un succès ou d’un échec). Pour résumer :<br />
<br />
Si g(x) donne une erreur, alors (f . g)(x) = g(x)<br />
Si g(x) réussit, alors (f . g)(x) = f(g(x))<br />
<br />
Pour achever la définition d’une catégorie, nous devons choisir les flèches de Kleisli « identité ». Ce sont celles qui ne font rien du tout, donc qu’on peut composer avec une autre flèche sans la modifier. Les identités sont des fonctions de A vers Err(A) et il se trouve qu’elles sont simplement en l’occurrence les fonctions f(x) = x, comme pour les ensembles. Remarquez qu’elles ne renvoient jamais d’échec, mais toujours des succès.<br />
<br />
Je passerai plus rapidement sur les trois exemples suivants, mais j’encourage les lecteurs qui ne trouvent pas le sujet encore assez clair à les travailler plus en détail et à utiliser cette opportunité de se familiariser avec la définition d’une flèche de Kleisli.<br />
<br />
Pour les flèches de Kleisli « dépendance », qui sont des fonctions de A à Param(B), souvenez-vous qu’elles équivalent à ajouter un paramètre représentant les préférences de l’application. L’idée maîtresse est que, si mes deux fonctions ont besoin de connaître les préférences de l’application, je dois donner le même paramètre aux deux. Donc composer ces deux flèches de Kleisli construit une nouvelle fonction qui reçoit les préférences comme paramètre et les transmet à ses deux composantes. Quant aux identités, il s’agit de flèches de Kleisli qui reçoivent le paramètre « préférences » mais l’ignorent et renvoient leur valeur d’entrée.<br />
<br />
Les flèches de Kleisli « incertitude » ou « non-déterminisme » sont des fonctions de A à P(B), l’ensemble des sous-ensemble de B. Cette fois-ci l’idée est d’essayer toutes les valeurs générées à chaque étape et de collecter l’ensemble des résultats. Donc la composition calcule la deuxième fonction pour chacun des résultats de la première et les résultats sont fusionnés par union des ensembles. Les identités, bien sûr, ne sont pas réellement non-déterministes mais retournent des ensembles d’un élément contenant leur valeur d’entrée.<br />
<br />
Enfin, les flèches de Kleisli pour les effets destructeurs sont des fonctions de A à IO(B). Là, l’idée est de combiner les instructions en les suivant étape par étape : la première d’abord, puis la suivante. Donc la composition consiste à écrire les instructions nécessaires à la première action, puis à la deuxième, dans cet ordre. Une flèche de Kleisli « identité » est l’instruction de ne rien faire et de renvoyer la valeur d’entrée comme résultat. <br />
Ainsi, pour chacun de ces problèmes, nous avons créé une nouvelle catégorie de Kleisli.<br />
<br />
Ces nouvelles catégories ont chacune en propre leurs identités, composition et autres aspects des fonctions, qui expriment la nature d’un problème spécifique. En utilisant la notion de composition dans la flèche de Kleisi appropriée, vous pouvez résoudre n’importe lequel de ces problèmes anciens de l’informatique de façon aisée et modulaire.<br />
<br />
Et voilà la raison pour laquelle il faut s’intéresser aux monades.<br />
<br />
Aux monades ? ? ! Ah, oui, il faut quand même que je précise que vous venez de découvrir les monades. Simplement sans utiliser le mot.<br />
<br />
<b>Quel est le rapport avec les monades ?</b><br />
Cette section est destinée à ceux qui veulent connaître les relations entre ce que nous venons de décrire et les monades telles qu’elles sont définies en mathématiques. Si vous ouvrez Wikipedia ou un manuel de théorie des catégories, ce que vous y rencontrerez sera assez différent de ce que nous venons de voir. Vous entendrez parler d’endo-foncteurs, de deux transformations naturelles et de propriétés de commutation entre triangles et carrés.<br />
<br />
Nous n’avons pas du tout parlé de foncteurs, et encore moins de transformations naturelles… donc comment aurions-nous pu apprendre ce que sont les monades ? Il s’avère qu’il existe plus d’une façon de décrire les monades. Celle que nous avons utilisée est tout à fait valide. Les déplacements que nous avons fait subir aux domaines d’arrivée de nos fonctions –Err, Param, P et IO- sont véritablement des exemples de monades. Pour s’assurer que ce sont des monades au sens mathématiques, il faudrait travailler dur : prouver que ce sont des foncteurs, élaborer deux transformations naturelles &#61544; et &#61549; et prouver qu’elles sont naturelles, et enfin prouver les trois lois des monades.<br />
<br />
Heureusement, il existe une façon plus simple d’y parvenir. Heinrich Kleisli, que nous avons déjà rencontré, a montré que, s’il est possible de construire une catégorie comme celles de la section précédente, dont les flèches sont simplement des fonctions au domaine d’arrivée modifié, alors il est garanti que votre catégorie constitue également une monade. Ce qui est bien pratique, parce que, comme programmeurs, nous nous intéressons nettement plus à nos flèches de Kleisli qu’au concept mathématique de monade. Rappel : ces flèche de Kleisli sont exactement cette variation sur la notion des fonctions que nous avons utilisée, bien avant de parler de théorie des catégories ! Et Kleisli nous démontre que, tant que la composition fonctionne comme attendu pour nos flèches de Kleisli (associativité + identité), il n’est plus besoin de prouver quoique ce soit d’autre pour être sûr de disposer d’une monade.<br />
<br />
Cela reste intéressant, à titre auxiliaire, d’étudier la relation entre les deux. Je ne donnerai pas tous les détails mais au moins la structure et laisserai au lecteur intéressé, un peu familier avec la théorie des catégories, le soin de faire la preuve des propriétés pertinentes. Nous utiliserons Err comme monade, pour choisir un des exemples –mais rien n’est spécifique à Err.<br />
<br />
Nous commençons avec Err, qui est déjà une application d’un ensemble vers un autre. Mais la définition traditionnelle demande également que ce soit un foncteur. Donc, si j’ai une fonction f de A à B, j’ai besoin de pouvoir construire une fonction Err(f) de Err(A) à Err(B). Je peux le faire de la façon suivante : dans la catégorie sous-jacente (la catégorie des ensembles, pas la catégorie de Kleisli), je prends une fonction d’identité de Err(A) à Err(A) ; puis je prends l’identité de Kleisli de B à Err(B). Je compose cette identité de Kleisli avec f pour parvenir à une fonction de A vers Err(B). Je peux donc utiliser la composition de Kleisli entre Err(A) -&gt; Err(A) et A -&gt; Err(B) pour parvenir à Err(A) -&gt; Err(B), c’est-à-dire Err(f).<br />
Ensuite, j’ai besoin d’une transformation naturelle &#61544;, provenant du foncteur identité vers Err : rien de compiqué, les composantes de &#61544; sont les identités de Kleisli.<br />
Enfin, je dois trouver une transformation naturelle µ de Err² à Err. Pour accéder au composant de µ auprès de A, je prends la fonctions d’identité de la catégorie sous-jacente de Err(Err A) vers Err(Err A), puis de Err(A) vers Err(A) et je les combine avec la composition de Kleisli pour obtenir la fonction de Err(Err A) à Err A. Voilà le composant de µ.<br />
<br />
La construction dans le sens opposé est nettement plus facile. En partant d’une monade Err avec &#61544; et µ, la catégorie de Kleisli est construite de la façon suivante : <br />
- les identités sont les composantes de &#61544;<br />
- soient une fonction f de A à Err(B) et une fonction g de B à Err(C), je les compose par : µ . Err(g) . f<br />
<br />
Encore une fois, les détails des preuves sont laissées au lecteur. J’espère que ce bref détour aura été utile. Dorénavant, j’utiliserai le mot « monade », mais entends à nouveau les monades via les catégories de Kleisli.<br />
<br />
<b>Rejoindre la révolution monadique</b><br />
Récapitulons une fois de plus :<br />
- les programmeurs travaillent par compositions de choses qu’on appelle fonctions<br />
- ces fonctions ne sont pas des fonctions au sens usuel, mais forment une catégorie<br />
- en fait, ce sont bien des fonctions au sens usuel, mais uniquement si l’on en transforme les domaines d’arrivée en quelque chose d’étrange<br />
- la catégorie qu’elles forment est appelée une catégorie de Kleisli, c’est-à-dire en fait une autre façon de voir les monades<br />
- ces monades décrivent de façon satisfaisante les techniques utilisées pour résoudre des problèmes pratiques<br />
<br />
Les quatre exemples cités n’épuisent pas le sujet. Ils sont représentatifs de beaucoup, beaucoup d’autres idées qui peuvent être décrites dans le même cadre de pensée. Il me paraît démontré, à ce stade, que celui qui cherche à étudier et analyser les langages de programmation devrait être familier avec certaines idées de la théorie des catégories, et avec les monades en particulier.<br />
<br />
Mais qu’en est-il de l’humble développeur, qui n’est pas en train de créer un nouveau langage, ne publie pas d’articles sur l’analyse des langages, mais veut simplement résoudre les problèmes qui se posent à lui tous les jours ? On est en droit de poser la question. Tant que les monades resteront un simple formalisme mathématique pour comprendre ce que les programmeurs entendent par fonction, des préoccupations pratiques justifieront de ne pas chercher à les comprendre.<br />
<br />
Il devient de plus en plus clair, cependant, que les monades ont entamé leur chemin vers les question pratiques de la programmation. Dans le passé, les flèches de Kleisli, cette forme modifiée des « fonctions », était déjà utilisée pour construire les langages de programmation. Les fonctions en C utilisaient une certaine flèche de Kleisli, et les fonctions en C++ une autre. Le standard du langage indiquait ce qui était possible et ce qui ne l’était pas, et si nous voulions quelque chose de différent, tant pis. Chaque décennie, peut-être, nous passions à un langage tout neuf et lézardions aux rayons d’une nouvelle caractéristique pour quelque temps.<br />
<br />
<b>Le passé : gestion des erreurs</b><br />
Prenons l’exemple de la monade Err, qui nous fournit des fonctions qui peuvent échouer et faire part de leur échec de façon structurée : réserve faite de quelques détails et extensions, il s’agit au fond de la gestion structurée des exceptions. Les programmeurs ont travaillé sans disposer de gestion des exceptions dans leur langage pendant de nombreuses années. Bien sûr, des langages comme le C sont Turing-complets et peuvent résoudre n’importe quel problème de calcul, y compris la gestion des erreurs. Mais nous n’utilisons pas le concept de catégorie pour penser les calculs possibles : il sert à penser la composition. Sans gestion des erreurs au sein même de la notion de fonction adoptée par des langages comme le C, la composition restait à la charge des programmeurs, « à la main ».<br />
<br />
En conséquence, une fonction C qui pouvait échouer devait indiquer un échec dans sa valeur de retour. Dans de nombreux cas, la convention était : « les valeurs de retour ne sont pas là pour indiquer le résultat, mais le succès ou l’échec ». Les bonnes pratiques appelaient à faire suivre chaque appel de fonction ou presque d’instructions de vérification du succès, et le code en devenait à peine lisible. C’était le temps des algorigrammes et autre pseudo-code, parce qu’on n’espérait plus que le code soit lisible au premier regard ! En réalité, en fait, les programmeurs ne testaient le succès de leur fonction que lorsqu’ils croyaient l’échec possible, et de nombreuses erreurs n’étaient pas détectées. Les programmes n’étaient souvent pas fiables et un nombre inconnu de milliards a été probablement dépensé en développement supplémentaire et en correction de bugs.<br />
<br />
Pourquoi cela ? C’est assez simple : parce que le C et les autres langages du temps reposaient sur un type de flèche de Kleisli insuffisant ! Si leur flèche de Kleisli avait inclus les fonctionnalités de la monade Err que nous avons définie, cela aurait pu être évité. Mais le concept de fonction supporté par le C étant fixe, la seule solution était de faire avec, et finalement de migrer vers un autre langage de programmation, avec réécriture de tout un tas de logiciels, et dépense d’un nouveau nombre inconnu de milliards.<br />
<br />
<b>Le présent : variables globales et contexte</b><br />
Qu’en est-il de la monade Param et des autres évoquées ? Comme nous l’avons dit, il s’agit de définir des opérations ayant accès à une information extérieure et à l’état du reste du monde.<br />
<br />
Dans le passé, nous utilisions des variables globales, l’équivalent à peine plus moderne du stockage d’information à un endroit connu de la mémoire. Vite fait, mal fait, et les programmeurs d’il y a 30 ans savaient déjà que ce n’était pas une solution satisfaisante, impraticable pour des applications de plus grande taille. La programmation orientée objet a permis d’atténuer ce problème, en plaçant les fonctions dans un « objet » qui leur sert de contexte et qui est transmis implicitement. Pour obtenir ce résultat, il a fallu changer de langage de programmation pour améliorer, une fois de plus, la flèche de Kleisli utilisée. Néanmoins, la solution apportée par les langages orientés-objet reste imparfaite.<br />
<br />
<b>Le futur proche (/présent) : pureté, effets, parallélisme, non-déterminisme, continuations et plus !</b><br />
Je parle ici au futur, mais tout est déjà possible, à condition de choisir le langage approprié ! <br />
<br />
Un des défis adressés à la communauté des programmeurs est de trouver une façon efficace de gérer le parallélisme. Paradoxalement, tandis que les exemples précédents montraient des problèmes causés par une flèche de Kleisli pas assez puissante, cette fois-ci le problème est à l’opposé. Les fonctions nues (ou pures) offrent un grand nombre d’opportunité pour le parallélisme. Lorsqu’elles sont exécutées en parallèle, cela peut-être plus rapide, cela peut-être aussi plus lent si le parallélisme est mal conçu, mais elles donneront de toute façon le même résultat. Mais si la flèche de Kleisli incorpore les modifications destructrices, ce n’est plus le cas : le parallélisme est risqué et peu donner des résultats incorrects en raison de ce qu’on appelle les accès concurrents (« race conditions »).<br />
<br />
Cependant, il est impossible de retirer les modifications destructrices de la flèche de Kleisli d’un langage. Un programme qui n’a pas d’effets observables n’est pas utile, tout simplement. Ce qu’il faut pouvoir faire est de séparer les parties du code qui effectuent des modifications destructrices de celles qui opèrent avec des fonctions pures. Donc, ce dont nous avons besoin, c’est d’un langage qui possède plusieurs sortes de flèches de Kleisli !<br />
<br />
Il existe déjà au moins un langage qui offre cette possibilité. Les utilisateurs d’Haskell peuvent créer leurs propres monades et travailler dans la catégorie de Kleisli de leu choix. La langage dispose d’une syntaxe qui rend cette approche lisible et aisée. Si une fonction peut échouer, on la met dans Err ; si elle a besoin d’accéder aux paramètres de l’application, dans Param ; de procéder à des entrées/sorties, dans IO. Les frameworks pour des applications web et d’autres projets similaires commencent par définir la monade appropriée pour ce qu’ils ont à faire.<br />
<br />
Une autre tendance de la communauté des développeurs, en ce moment, est de créer davantage de modèles de programmation spécifiques à un domaine. Le langage Erlang est devenu populaire parce qu’il fournit un modèle de programmation avantageux pour le parallélisme. Le framework .NET propose LINQ, qui offre un modèle de programmation efficace pour traiter et requêter des collections de données. Rails a popularisé les langages spécifiques au domaine de la programmation web. D’autres langages proposent des continuations pour construire des opérations d’une manière plus flexible. Tous ces exemples montrent le besoin de travailler avec les flèches de Kleisli appropriées à la tâche entreprise.<br />
<br />
Au fond, si nous croyons qu’il existe une seule notion de « fonction » appropriée pour l’ensemble des programmes informatiques, et qu’un langage existant la définit, nous pouvons rejeter, en tant que programmeurs, les idées générales de monade et de flèche de Kleisli comme une lubie de théoriciens. Mais nous n’en prenons pas le chemin. La communauté des programmeurs s’est engagée résolument dans une direction où plusieurs définitions de la fonction coexistent, selon le contexte, la tâche à accomplir, ou même pour une application donnée. C’est pourquoi il est important de disposer du langage, des outils, et des intuitions nécessaires à la comparaison de ces abstractions. Tout cela, les monades nous les donnent.<br />
<b><br />
Les abstractions construites sur les monades</b><br />
Un langage qui permet de choisir ses monades offre encore d’autres avantages, en particulier un nouveau niveau d’abstraction. En Haskell, par exemple, il est possible d’écrire du code applicable à différentes monades. De façon surprenante, une part importante du code écrit pour une certaine monade fait sens pour d’autres monades également. Prenons par exemple le type Haskell suivant :<br />
<br />
sequence : : Monad m =&gt; [m a] -&gt; m [a]<br />
<br />
Cela signifie, pour une monade M quelconque, que sequence convertit une liste de valeurs M(A) en M(List A), c’est-à-dire applique la monade à la liste elle-même. Prenons une minute pour en voir la signification pour nos quatre exemples. Pour Err, sequence prend une liste de résultats qui pourraient être un échec, et si l’un d’entre eux est un échec, toute la séquence échoue. C’est simplement une façon pratique de vérifier l’échec d’une liste complète d’opérations. Pour Param, sequence prend un ensemble unique de préférences, le distribue à tous les éléments d’une liste et retourne une liste de résultats. Pour la monade P (ensemble des sous-ensembles), sequence prend une liste d’ensembles et retourne l’ensemble de toutes les façons de choisir un élément par ensemble. Enfin pour IO, il prend une liste de listes d’instruction, et retourne une liste unique de toutes les instructions à réaliser dans l’ordre.<br />
Une unique fonction, avec une seule implémentation, fait sens –et quelque chose d’utile- pour nos quatre exemples de monades !<br />
<br />
Avec le choix des monades vient la capacité de s’abstraire de ce choix et d’écrire du code significatif pour n’importe laquelle des monades que l’on choisira.<br />
<br />
Voyant toutes ces forces à l’œuvre, je prédis que, d’ici dix ans, on attendra des développeurs logiciels qu’ils discutent des monades de la même façon qu’on attend d’eux aujourd’hui qu’ils discutent des design patterns ou des méthodologies agiles.<br />
<br />
<b>Au-delà des monades : plus de programmation par les catégories</b><br />
Même si la plupart de ce qui a été dit concerne les monades, je ne voudrais pas laisser l’impression qu’elles sont la seule preuve de l’influence de la théorie des catégories sur la programmation. Toutes les idées ci-après on fait leur chemin dans la pratique de la programmation, le plus souvent (jusqu’ici) dans la communauté Haskell à cause de la flexibilité du langage et de ses profondes racines académiques.<br />
<br />
Les transformateurs de monade constituent une technique puissante pour combiner les effets de plusieurs monades et construire des modèles de programmation variés et performants.<br />
Les foncteurs et les foncteurs applicatifs sont moins puissants que les monades, mais d’une utilisation encore plus large.<br />
D’autres genres de catégories, qui ne sont pas des catégories de Kleisli peuvent souvent être définies pour résoudre des problèmes spécifiques, dont les catégories de Freyd.<br />
<br />
Je m’arrête ici, mais uniquement pour vous encourager à rechercher vous-mêmes les abstractions variées de la théorie des catégories que les programmeurs ont trouvées utiles. Un bon point de départ est la Typeclassopedia (spécifique à Haskell) de Brent Yorgey. Et ce n’est qu’une porte donnant sur les possibilités nombreuses d’application de la théorie des catégories.<br />
<br />
J’espère avoir été capable de convaincre que ces idées n’ont pas été simplement inventées mais sont l’extension naturelle de ce qu’ont fait les programmeurs pendant des décennies.</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b459/quoi-peuvent-bien-servir-monades/</guid>
		</item>
		<item>
			<title>Une petite cuillère de types (Haskell)</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b457/petite-cuillere-types-haskell/</link>
			<pubDate>Wed, 22 Apr 2015 09:37:04 GMT</pubDate>
			<description>Ce billet sera court et sans...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Ce billet sera court et sans grande difficulté. Il servira de marche-pied ou d'aide-mémoire pour les billets suivants, qui présenteront un des aspects les plus excitants mais aussi les plus abstraits de la programmation en Haskell: l'apport de la <a href="http://fr.wikipedia.org/wiki/Th%C3%A9orie_des_cat%C3%A9gories" target="_blank">théorie des catégories</a> au développement d'applications. Je tenterai ici de vous présenter, aussi clairement que possible, le système de types de Haskell, à la fois souple et puissant.<br />
<br />
<b>Ce que vous savez déjà</b><br />
- Haskell possède un système de type statique (la régularité est vérifiée à partir du code, pas pendant l'exécution) et strict (aucune conversion automatique n'est acceptée).<br />
- Le compilateur et l'interpréteur sont capables, sauf ambiguïté du programme, d'inférer les types utilisés à partir d'éléments de contexte (ex: fonctions appelées sur les arguments)<br />
- la syntaxe basique d'une déclaration de type est: <br />
<br />
<font color="#800000"><div style="margin-left:40px">nom :: Type</div></font><br />
Par exemple:<br />
<font color="#000080"><br />
<div style="margin-left:40px">&gt; pi :: Double</div></font><br />
Pour les fonctions, on a:<br />
<br />
<font color="#800000"><div style="margin-left:40px">nomDeFonction :: arg1 -&gt; arg2 -&gt; ... -&gt; argN -&gt; returnType</div></font><br />
Par exemple:<br />
<br />
<font color="#000080"><div style="margin-left:40px">&gt; integerSum :: [Integer] -&gt; Integer</div></font><br />
<b>Ce que vous allez apprendre</b><br />
Le système de types Haskell est plus souple et plus puissant que cela. Il permet d'utiliser des types génériques, plus ou moins semblables aux templates du C++, mais également d'ajouter des contraintes de type: pour reprendre l'exemple du C++, ces contraintes seraient l'équivalent des &quot;<a href="http://en.wikipedia.org/wiki/Concepts_%28C%2B%2B%29" target="_blank">concept</a>s&quot; qu'il est envisagé d'ajouter dans une norme future du langage. Enfin, Haskell permet de créer de nouveaux types, des alias de types et enfin de les &quot;recouvrir&quot;.<br />
<br />
<b>Les types génériques</b><br />
La fonction integerSum que nous avons déclarée est sympathique, mais si on lui donne une liste de Float, elle la refusera sèchement. Pourtant, l'algorithme pour additionner les membres d'une liste de Float ou de Double n'est pas différent. Aussi existe-t-il la possibilité de donner un type générique aux arguments d'une fonction:<br />
<br />
<font color="#000080"><div style="margin-left:40px">&gt; sum :: [a] -&gt; a</div></font><br />
<i>NB: contrairement aux types concrets, les types génériques commencent par une <u>minuscule</u>.</i><br />
<br />
Les lecteurs attentifs se doutent que la fonction sum fait appel à un opérateur (+), qui aurait donc comme signature:<br />
<br />
<font color="#000080"><div style="margin-left:40px">&gt; (+) :: a -&gt; a -&gt; a</div></font><br />
Nous voilà dans une situation insatisfaisante: on voulait s'éviter d'écrire une fonction sum pour chaque type numérique, et nous voilà avec un opérateur d'addition qui accepte n'importe quel type: des connexions à des bases de données, des arbres binaires... La seule contrainte est que chaque membre de l'addition, et le résultat, soient de même type (celui que 'a' représente).<br />
<br />
<b>Les classes de types </b>(<i>typeclasses</i>)<br />
Haskell permet de créer des familles de types, qui peuvent être ensuite utilisées pour placer des contraintes de types sur les arguments génériques d'une fonction. Pour vous faire une idée des familles prédéfinies, en voilà la hiérarchie en une image:<br />
<br />
<img src="https://www.developpez.net/forums/attachments/p175636d1429694640/environnements-developpement/delphi/edi/kylix-demarrer-cliquant-l-executable/classes.gif/" border="0" alt="Nom : classes.gif
Affichages : 3129
Taille : 10,2 Ko"  style="float: CONFIG" /><br />
<br />
<i><br />
NB: vous pouvez retrouver cette image et des informations complémentaires <a href="https://www.haskell.org/onlinereport/basic.html" target="_blank">ici</a>.</i><br />
<br />
La syntaxe pour créer une classe est la suivante:<br />
<br />
<div style="margin-left:40px"><font color="#800000">class NomDeMaClasse where<br />
<div style="margin-left:40px">fonction1 :: arg1 -&gt; ... -&gt; returnType<br />
fonction1 =<br />
fonction2 :: arg1 -&gt; ... -&gt; returnType<br />
fonction2 =</div></font><br />
...</div>Un exemple simple est la classe Eq:<br />
<br />
<font color="#000080"><div style="margin-left:40px">class Eq a where<br />
<div style="margin-left:40px">(==) :: a -&gt; a -&gt; Bool<br />
x == y = if x /= y then False else True<br />
(/=) :: a -&gt; a -&gt; Bool<br />
x /= y = if x == y then False else True</div></div></font><br />
Un type est une instance de la classe Eq si elle implémente au moins l'une des deux fonctions (remarquez en effet que chacune est définie par rapport à l'autre). Comme le nom de ces fonctions est déjà pris (par la classe Eq), un mécanisme d'instanciation est nécessaire -plus simplement, il faut déclarer que notre type est une instance de cette classe. Par exemple, pour un type User:<br />
<br />
<font color="#000080"><div style="margin-left:40px">instance Eq User where<br />
<div style="margin-left:40px">(==) = sameId <font color="#006400">-- est vrai si les deux utilisateurs ont le même id</font></div></div></font><br />
Lors de la déclaration d'une fonction, nous pouvons désormais exiger qu'un type abstrait soit l'instance d'une classe. La syntaxe est:<br />
<br />
<font color="#800000"><div style="margin-left:40px">nomDeFonction :: constraint1 Class NomDe Type, constraint2 Class NomDe Type, ... =&gt; NomDeType -&gt; NomDeType -&gt; ...</div></font><br />
Prenons l'exemple d'une fonction filtre, qui conserve les éléments d'une liste égaux à son premier argument:<br />
<font color="#000080"><br />
<div style="margin-left:40px">filter :: Eq a =&gt; a -&gt; [a] -&gt; [a]<br />
filter _ [] = []<br />
filter n (x : xs) = if x == n then x : (filter n xs) else filter n xs</div></font><br />
<b>Créer de nouveaux types</b><br />
Cette possibilité n'est réellement intéressante que conjuguée à la création de nouveaux types -et Haskell le permet. Si Haskell était dépourvu d'un type Bool, on pourrait en créer un ainsi:<br />
<br />
<font color="#000080"><div style="margin-left:40px">data Bool = True | False</div></font><br />
<br />
- data est le mot clé qui introduit la création d'un nouveau type<br />
- Bool est le nom du nouveau type<br />
- True et False sont les constructeurs d'instances de ce type<br />
- et le symbole pipe ( | ) sépare les constructeurs <br />
<br />
Avec des constructeurs sans argument, on est tout de même un peu limité. Pour reprendre notre type User, il pourrait être défini ainsi:<br />
<br />
<font color="#000080"><div style="margin-left:40px">data User = User Integer String</div></font><br />
- le nom du constructeur et du type peuvent être identiques<br />
<br />
Un nouvel utilisateur peut être alors créé:<br />
<br />
<div style="margin-left:40px"><font color="#000080">&gt;</font> let myself = User 123 &quot;stendhal666&quot;</div><br />
Mieux encore, un constructeur peut faire référence au type qu'il construit, afin de créer un type récursif. L'exemple canonique est celui de la liste:<br />
<br />
<font color="#000080"><div style="margin-left:40px">data Liste a = Vide | Cons a (Liste a)</div></font><br />
- Liste est un type abstrait, qui doit être paramétrisé par un autre type (le type des éléments qu'elle contient)<br />
- a est le nom du type (générique) qui paramétrise le type Liste<br />
<br />
On pourrait donc créer une de ces listes ainsi:<br />
<br />
<div style="margin-left:40px"><font color="#000080">&gt;</font> Cons 'h' Vide</div><br />
ou encore:<br />
<br />
<div style="margin-left:40px"><font color="#000080">&gt; </font>Cons h (Cons a (Cons s (Cons k (Cons e (Cons l (Cons l Vide))))))</div><br />
<b>Conclusion:</b><br />
Nous n'avons pas encore couvert tout le système de type de Haskell, mais nous avons vu les aspects les plus intéressants. Les alias ne présentent pas de difficulté particulière: le mot-clé <i>type</i> remplace le mot-clé <i>typedef</i> du C/C++, essentiellement. La &quot;couverture&quot; de type est une notion un peu plus avancée dont les avantages n'apparaissent pas à ce stade: il me semblait donc inutile de surcharger cette cuillère, qui est déjà une bonne cuillère à soupe...<br />
<br />
<u>Exercices:</u><br />
- dans l'interpréteur GHCI, la commande :t suivie d'une expression renvoie le type de cette expression. Regardez le type de quelques fonctions. En particulier, le type de l'opérateur de composition (.) ou d'application $, ou encore le type de différentes fonctions numériques<br />
- définir un type BinaryTree<br />
- quels sont pour vous les avantages des différents systèmes de types?</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b457/petite-cuillere-types-haskell/</guid>
		</item>
		<item>
			<title>Babel</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b447/babel/</link>
			<pubDate>Fri, 17 Apr 2015 08:23:02 GMT</pubDate>
			<description><![CDATA[[tr] 
	[td][/td] 
	[td][/td]...]]></description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><div class="cms_table"><table width="500" align="right" class="cms_table"><tr valign="top" class="cms_table_tr"><td class="cms_table_td"></td>
<td class="cms_table_td"></td>
<td class="cms_table_td"><i>Toute la terre avait une seule langue et les mêmes mots. (...)  Ils dirent encore: «Allons! Construisons-nous une ville et une tour dont le sommet touche le ciel et faisons-nous un nom afin de ne pas être dispersés sur toute la surface de la terre.» L'Eternel descendit pour voir la ville et la tour que construisaient les hommes, et il dit: «Les voici qui forment un seul peuple et ont tous une même langue, et voilà ce qu'ils ont entrepris! (...) Allons! Descendons et là brouillons leur langage afin qu'ils ne se comprennent plus mutuellement.» (...) Alors ils arrêtèrent de construire la ville. C'est pourquoi on l'appela Babel: parce que c'est là que l'Eternel brouilla le langage de toute la terre et c'est de là qu'il les dispersa sur toute la surface de la terre.</i> <b>Genèse 11 1-9</b></td>
</tr>
</table></div>
<br />
Connaître plusieurs langages est-il souhaitable? <a href="http://www.developpez.net/forums/d1511498/general-developpement/debats-developpement-best-of/developpeur-apprendre-plusieurs-langages/" target="_blank">Ce débat animé</a> trouve dans la Genèse une réponse tranchante: l'unité rend l'homme égal à Dieu; la langue adamique, vestige du Paradis perdu, est rayée de la surface de la terre pour châtier l'orgueil des hommes que la dispersion réduit à l'impuissance. Il est vrai que les avantages d'un langage unique sont évidents: compréhensible par tous, compatible avec tout, pérenne, il rend les développeurs pour ainsi dire interchangeables. Plus de code mort, dont l'intelligibilité a disparu avec le créateur, plus d'erreurs ni de manque dans les bibliothèques mille fois testées! Tout le reste n'est que jouet de geek ou lubie de chercheur...<br />
<br />
En comparaison, les avantages de la multiplicité des langages sont obscurs. La fragmentation paraît bien un châtiment, une malédiction. Pourquoi alors s'y obstiner? Qu'est-ce qui pousse des programmeurs respectables à en explorer, voire à en créer toujours de nouveaux? Ce billet est une tentative de répondre à la question: j'y dénonce le mythe du langage unique -une incompréhension profonde de ce qu'est un langage informatique- pour en tirer quelques leçons et une grande fierté pour les programmeurs.<br />
<br />
<b>Pourquoi un langage, c'est déjà plusieurs langages</b><br />
La création de mots est l'activité principale du développeur: création de nouveaux types (substantifs), de nouvelles fonctions (verbes), de nouvelles macros (constructions grammaticales)... Défini par sa syntaxe et ses mots-clés, un langage informatique est plutôt un méta-langage, un outil pour créer d'autres langages aux vocabulaires et aux tournures variées. Cette caractéristique des langages de programmation n'est peut-être pas apparente dans les programmes les plus triviaux; mais dès que le code s'allonge et devient plus complexe, et d'autant plus s'il est une affaire d'équipe, il devient évident que chaque bibliothèque, chaque application crée son propre langage -un langage nouveau qui doit s'apprendre.<br />
<br />
L'importance du langage naturel dans le code vient précisément de ce phénomène. Que ce soit sous la forme de documentation, de commentaires, des noms de variable, il témoigne du fait que le langage utilisé n'est que l'hôte d'un langage nouveau et que le connaître ne suffit pas à comprendre le code dont il fixe les règles. Si le paroxysme de ce processus de création est atteint dans les Domain Specific Languages, il n'y a entre eux et le reste des programmes qu'une différence de degré, pas de nature. Autrement dit, le langage unique n'est dans une certaine mesure qu'une fiction.<br />
<br />
La complexité croissante des programmes, le recours aux frameworks, à la création automatique de code -que ce soit par des interfaces XML ou dans le cadre de la programmation orientée aspect, par exemple- ne fait que renforcer cette hétérogénéité entre le langage lui-même et les langages qu'il abrite; et, d'un certain point de vue, les standards de qualité, les design patterns et les fonctionnalités des EDI se surajoutent encore au vocabulaire que doit maîtriser un développeur qui intègre une équipe.<br />
<br />
<b>Les objections du &quot;pointy hairy boss&quot;</b><br />
Considérer qu'il n'existe pas de langage, mais toujours des langages, paraît néanmoins très intellectuel. L'objection vient naturellement: quand bien même chaque programme créerait un langage différent, il reste plus facile à lire -et à écrire- pour quelqu'un qui est familier avec le langage hôte. Cet argument est pourtant imparfait: il y a le risque des &quot;faux amis&quot;, qui trompent plus sûrement un connaisseur qu'un néophyte; et puis dans certains cas, des pans importants du langage hôte sont exprès recouverts: ainsi le framework Qt, qui fait l'impasse sur la plus grande partie de la librairie standard du C++. Surtout, cet argument est réversible: si connaître Java est censé faciliter la compréhension d'un programme écrit avec Hadoop, par exemple, on peut tout autant soutenir que connaître le C++ aide à comprendre Java (une affirmation du créateur de Java lui-même), le C C++, et tout langage impératif le C. Réciproquement, connaître des langages fonctionnels ou distribués permet de mieux comprendre un programme Java écrit à grand renfort de map, de reduce et de lambdas pour un  cluster de serveurs.<br />
<br />
Il est vrai que ces arguments risquent de ne pas convaincre un responsable de SI, préoccupé par des exigences immédiatement opérationnelles. A vrai dire, ce personnage qui hante les blogs de développeurs - le &quot;pointy hairy boss&quot;, chef pointilleux et poilu, pour une raison qui m'échappe - risque plutôt, en prenant conscience du risque de fragmentation, de réduire encore les options à disposition des développeurs, sous prétexte de productivité. Eh bien, tant pis! si on ne me laisse pas le choix, d'accord, mais je refuse de me couper les c... avant qu'on me l'ait demandé!<br />
<br />
<b>Les leçons à en tirer</b><br />
Si la diversité est inhérente à la programmation, autant en tirer profit. C'est ce qu'ont fait avec succès des entreprises comme IBM, qui utilise Prolog dans son système intelligent Watson, Facebook, Whatsapp ou Call of Duty qui ont choisi Erlang pour leurs applications de chat ou leurs serveurs de jeu. Un exemple plus ancien mais plus frappant encore d'une réussite fondée sur l'utilisation d'un langage &quot;à part&quot; est celui que raconte Paul Graham dans son article <a href="http://www.paulgraham.com/avg.html" target="_blank">Beating the averages</a>: c'est grâce aux particularités de LISP qu'il s'est maintenu devant la concurrence lorsqu'il a crée ce qui est devenu Yahoo Store aujourd'hui.<br />
<br />
Il ne faut bien sûr pas chercher la diversité à tout prix. Mais il faut l'aimer pour elle-même, pour ce qu'elle donne d'ouverture d'esprit et d'adaptabilité. La diversité est, qui plus est, en partie apparente: pour celui qui connaît les grandes familles de styles de programmation et de systèmes de types, la plupart des langages, quelle que soit leur nouveauté ou leur bizarrerie apparente, deviennent vite familiers. Ne nous restreignons pas à Java! Allons voir ces langages déclaratifs, fonctionnels, homoiconiques, à pile, à prototypes, objet, aspect, ces systèmes de types statiques, dynamiques, stricts ou souples, à types dépendants, génériques! <br />
<br />
De toute façon, l'expertise dans un langage ou un domaine particulier n'est possible qu'au prix d'une véritable culture générale. Prenez le C++: je suis frappé, sur le forum, du nombre de personnes qui ne songent pas à utiliser les algorithmes de la librairie standard et les nouveautés du C++11, parce qu'elles reposent sur des principes fonctionnels, les fonctions d'ordre supérieur et la composition. Comme le fait remarquer le créateur du langage, Bjarne Stroustrup, <a href="http://loic-joly.developpez.com/articles/5Mythes/" target="_blank">ce n'est pas un langage orienté objet</a> mais un langage traversé par différents paradigmes; sans culture générale, on n'en fera jamais qu'une utilisation limitée. L'exemple du C++ contient en fait plus encore d'enseignements: la méta-programmation, qui en est une part désormais constitutive, a été développée en grande partie grâce à la fécondation de la culture fonctionnelle; une bonne part du livre fondateur d'Andrei Alexandrescu, <i><a href="http://en.wikipedia.org/wiki/Modern_C%2B%2B_Design" target="_blank">Modern C++ design</a></i>, consacrée aux typelists, est directement inspirée de LISP. <br />
<br />
<b>Programmeurs de tous les pays, unissez-vous!</b><br />
<a href="http://www.corejavainterviewquestions.com/ispolyglotprogrammingagoodthing/" target="_blank">L'article à la base de la discussion sur le forum</a> affirme -combien de fois l'ai-je entendu- que la variété est un plaisir de geek, le caprice d'un enfant qui trouve un nouveau jouet sous le sapin. Que de mépris! Cette variété est le résultat de l'incorporation de réflexions sérieuses et interdisciplinaires. Le clivage entre les préoccupations académiques et celles de la production existe certainement à un certain stade de maturité d'un projet, mais l'histoire de l'informatique est celle de la fécondation de l'industrie par la recherche (la réciproque étant également vraie). L’interdisciplinarité crée, à la faveur des projets de recherche, des rapprochements inattendus: du calcul lambda à LISP, de LISP à l'intelligence artificielle, pour donner un exemple, dont le poids actuel est suffisant, je pense, pour récuser l'idée d'un caprice: un nouveau langage n'est pas le dernier iPhone mais le véhicule de concepts dont la portée, en gestation encore, pourrait être déterminante dans les années à venir.<br />
<br />
Je comprends que ce ne soient pas là les préoccupations d'un chef de SI. Mais il me semble que nous autres devons respecter notre art, et ne respecter les exigences du chef poilu qu'avec <a href="http://www.penseesdepascal.fr/C1-C2/C1p33v-C2p50-51-Raisons9.pdf" target="_blank">la pensée de derrière</a>. Tirons fierté de cette variété!</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b447/babel/</guid>
		</item>
		<item>
			<title>Intelligence du morpion - la suite</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b436/intelligence-morpion-suite/</link>
			<pubDate>Mon, 13 Apr 2015 10:21:14 GMT</pubDate>
			<description>N.B : Comme le précédent, ce...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>N.B : Comme le précédent, ce billet est largement inspiré de l’article de John Hugues :<a href="http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf" target="_blank"> Why functional programming matters</a>.</i><br />
<br />
Dans le billet <i><a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b426/19-bits/" target="_blank">19 bits</a></i>, nous avions posé les bases d'un jeu de morpion, en détaillant l'implémentation du plateau de jeu, de l'arbre des positions utilisée pour étudier les coups possibles et de l'évaluation statique d'une position. Les deux fonctions clés -à ce stade, les détails de l'implémentation du plateau ne sont plus importants- étaient les suivantes:<br />
<br />
<div style="margin-left:40px">buildGTree :: Int -&gt; GameTree<font color="#8B4513"> -- crée l'arbre de jeu en partant d'une position</font><br />
prune :: Int -&gt; GameTree -&gt; GameTree<font color="#8B4513"> -- taille l'arbre de jeu pour n'en conserver que n niveaux</font></div><br />
<b>L'algorithme minimax</b><br />
Evaluer une position de façon statique est assez limité: ne vous est-il jamais arrivé, concentrés que vous étiez sur une stratégie pour mettre en échec l'adversaire, d'oublier qu'il avait lui aussi une liberté de mouvement? Ou bien (je fais appel à des souvenirs de jeunesse) d'être obnubilés par la rangée de pièces rouges que vous constituiez au &quot;Puissance 4&quot; avant d'en être frustrés par un impeccable rang de pièces jaunes?<br />
<br />
L'algorithme minimax prend en compte les motivations de l'adversaire; il sait qu'il choisira le meilleur coup pour lui, pas pour vous. Si on assigne un score à une position, d'autant plus élevé qu'il vous favorise et d'autant plus bas qu'il favorise l'adversaire, vous choisirez la position qui a le score le plus élevé, et l'adversaire celle qui a le score le plus faible: regarder en avant dans la partie, c'est donc alterner maximum et minimum des positions possibles.<br />
<br />
La première étape est donc d'attribuer de façon statique un score à chaque position. Nous avons déjà la fonction d'évaluation, nommée à juste titre static. Il nous faut un moyen de l'appliquer à l'arbre de jeu:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0">mapTree :: (Int -&gt; Int) -&gt; GameTree -&gt; GameTree
mapTree f (Node n []) = Node (f n) [] <font color="#8B4513"> -- feuille</font>
mapTree f (Node n ns) = Node (f n) (map (mapTree f) ns) <font color="#8B4513"> -- branche</font></pre></td></tr></table></pre>
</div><br />
Avec:<br />
<br />
<div style="margin-left:40px">&gt; mapTree static . buildGTree $ pos</div><br />
On a donc l'arbre des évaluations statiques en partant d'une position<br />
<br />
On peut alors définir maximise et minimise:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0">maximise <span style="color: #339933;">:</span><span style="color: #339933;">:</span> GameTree -&gt; <span style="color: #0080ff;">Int</span>		
maximise <span class="br0">&#40;</span>Node n <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> = n
maximise <span class="br0">&#40;</span>Node <span style="color: #0000ff;">_</span> ns<span class="br0">&#41;</span> = <span style="color: #0080ff;">maximum</span> $ <span style="color: #0080ff;">map</span> minimise ns
&nbsp;
minimise <span style="color: #339933;">:</span><span style="color: #339933;">:</span> GameTree -&gt; <span style="color: #0080ff;">Int</span>
minimise <span class="br0">&#40;</span>Node n <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> = n
minimise <span class="br0">&#40;</span>Node <span style="color: #0000ff;">_</span> ns<span class="br0">&#41;</span> = <span style="color: #0080ff;">minimum</span> $ <span style="color: #0080ff;">map</span> maximise ns</pre></td></tr></table></pre>
</div><br />
<b>Modularité de la composition fonctionnelle et paresseuse</b><br />
Pour récapituler, nous pouvons désormais évaluer une position avec la fonction:<br />
<br />
evaluate :: Int -&gt; Int<font color="#8B4513"> -- lance la co-récursion minimise / maximise</font><br />
evaluate = maximise . mapTree static . prune 5 . buildGTree  <font color="#8B4513">-- 5 est en quelque sorte le niveau de difficulté du jeu</font><br />
<br />
Chaque composant est indépendant des autres: je peux remplacer presqu'à loisir, sans toucher à quoique ce soit d'autre, chacune des fonctions utilisées. Je peux utiliser une nouvelle fonction de maximisation -c'est ce que nous ferons tout de suite avec l'optimisation alpha-beta- ou une nouvelle fonction d'évaluation statique, une nouvelle fonction d'élagage de l'arbre, etc.<br />
<br />
Qui plus est, et contrairement aux apparences, grâce à l'évaluation paresseuse, <i>je ne traverse l'arbre qu'une seule fois</i>! Je peux donc rajouter des étapes d'optimisation ou de retraitement à ma convenance!<br />
<br />
<b>L'optimisation alpha-bêta</b><br />
Pour donner un exemple de cette modularité, nous allons réécrire la fonction de maximisation en y incluant l'optimisation alpha-bêta. Elle repose sur une observation simple, quoique contre-intuitive: pour déterminer le minimum maximal (et réciproquement le maximum minimal), il n'est pas nécessaire de parcourir toute la liste des minima (maxima). Cela permet d'éviter l'évaluation d'un certain nombre de coups qui ne se réaliseront que si l'adversaire veut se tirer une balle dans la jambe...<br />
<br />
Par exemple:<br />
<div style="margin-left:40px">&gt; maximum (minimum [2,1]) (minimum [0,<font color="#008000">5,-6,8,5,4,2,-1,-9,3,2,0,5,1,4,2</font>]) <br />
<font color="#8B4513">--il n'est pas nécessaire d'évaluer la partie en vert: de toute façon, zéro comme minimum potentiel est déjà inférieur au minimum de la première liste</font></div><br />
On peut capturer cette optimisation dans la fonction suivante:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0">alphaBetaMinimum :: [[Int]] -&gt; [Int]
alphaBetaMinimum (nums : rest) = (potentialMin) : (omitIf  (&lt;=) (potentialMin) rest)
<div style="margin-left:40px">where potentialMin = minimum nums</div>
omitIf p a [] = []
omitIf p a (xs:xss)
<div style="margin-left:40px">	| any (p a) xs = omitIf p a xss <font color="#8B4513">-- dès qu'un nombre est inférieur au minimum maximal connu, on passe</font>
	| otherwise = (minimum xs) : (omitIf p (minimum xs) xss)</div></pre></td></tr></table></pre>
</div><br />
Il ne nous reste plus qu'à créer la fonction d'appel:<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:108px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td valign="top"><pre style="margin: 0">abMaximise <span style="color: #339933;">:</span><span style="color: #339933;">:</span> GameTree -&gt; <span style="color: #0080ff;">Int</span>
abMaximise = <span style="color: #0080ff;">maximum</span> . <span style="color: #339933;">abMaximise'</span> 
&nbsp;
<span style="color: #339933;">abMaximise'</span> <span style="color: #339933;">:</span><span style="color: #339933;">:</span> GameTree -&gt; <span class="br0">&#91;</span><span style="color: #0080ff;">Int</span><span class="br0">&#93;</span>
<span style="color: #339933;">abMaximise'</span> <span class="br0">&#40;</span>Node n <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> = <span class="br0">&#91;</span>n<span class="br0">&#93;</span>
<span style="color: #339933;">abMaximise'</span> <span class="br0">&#40;</span>Node <span style="color: #0000ff;">_</span> subs<span class="br0">&#41;</span> = alphaBetaMinimum . <span style="color: #0080ff;">map</span> <span style="color: #339933;">abMinimise'</span> $ subs</pre></td></tr></table></pre>
</div><br />
et à modifier evaluate:<br />
<br />
<div style="margin-left:40px">evaluate = abMaximise . mapTree static . prune 8 . buildGTree  <font color="#8B4513">-- on va plus loin dans l'arbre, et plus vite!</font></div><br />
<b>Conclusion:</b><br />
Ce billet était le dixième de ce blog. J'espère avoir montré, au fur et à mesure, l'intérêt et peut-être la beauté d'une programmation fonctionnelle modulaire et concise. Je continuerai dans les billets suivants à alterner présentation de langages, de concepts et de cas pratiques. Je pense néanmoins, dans le prochain billet, me livrer à une réflexion plus générale sur la variété des langages et le sens de cette variété -en réaction, au moins partiellement, à <a href="http://www.developpez.net/forums/d1511498/general-developpement/debats-developpement-best-of/developpeur-apprendre-plusieurs-langages/" target="_blank">la très intéressante discussion en cours sur le forum</a>.<br />
<br />
<u>Exercices</u><br />
<div style="margin-left:40px">- une des optimisations possibles de l'algorithme minimax est de ne retenir que les n meilleurs coups à disposition de l'adversaire pour poursuivre l'évaluation. Donnez un exemple d'implémentation.<br />
- quelles modifications imposerait l'application de l'algorithme au jeu de dames? Quelles fonctionnalités pourrait offrir un langage pour les minimiser?</div><br />
<i>Pour faciliter l'expérimentation, le code source consolidé est disponible en pièce-jointe (extension en .txt car les fichiers haskell ne sont pas acceptés par le site !! / à transformer en .hs avant utilisation)</i><a href="https://www.developpez.net/forums/attachments/p174609d1428920094/c-cpp/c/resize-d-image/morpions.txt/"  title="Nom : morpions.txt
Affichages : 110
Taille : 4,4 Ko">morpions.txt</a></blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b436/intelligence-morpion-suite/</guid>
		</item>
		<item>
			<title>19 bits</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b426/19-bits/</link>
			<pubDate>Tue, 07 Apr 2015 07:59:00 GMT</pubDate>
			<description>N.B : Ce billet est largement...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><i>N.B : Ce billet est largement inspiré de l’article de John Hugues :<a href="http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf" target="_blank"> Why functional programming matters</a>.</i><br />
<b><br />
Madame la Marquise m’a foutu les morpions…</b><br />
Dans un <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b405/origami/" target="_blank">précédent billet</a>, je promettais une courte série sur une A.I. simple et modulaire pour jeux de plateaux, sous la forme d’un hommage aux morpions. Cette A.I. met à profit les possibilités de composition de fonctions et d’évaluation paresseuse introduites dans les précédents billets.<br />
<br />
<b>Un plateau de morpions</b><br />
J’ai décidé de faire tenir mon plateau de jeu dans un entier. Aucune nécessité à cela, bien entendu, mais j’en profite pour montrer que l’optimisation n’est pas étrangère aux langages « de haut niveau » qui peuvent très bien travailler avec des bits.<br />
Un plateau est donc décomposé de la façon suivante :<br />
<ul><li style="">Bits 0-8 : les jetons du joueur (on a qu’à dire les croix)</li><li style="">Bits 9-17 : les jetons de l’ordinateur (donc les ronds)</li><li style="">Bit 18 : 0 si c’est le tour du joueur, 1 pour l’ordinateur.</li></ul><br />
<br />
<b>Un arbre de plateaux</b><br />
Pour que l’A.I puisse faire des coups éclairés, il faut qu’elle puisse se projeter dans le déroulement de la partie. La façon classique de le lui permettre est de créer un « arbre de jeu », dans lequel chaque position qu’il est possible d’atteindre à partir d’un plateau p est son enfant :<br />
<br />
<div style="margin-left:40px">p</div><div style="margin-left:40px"> /   |  \</div><div style="margin-left:40px">e1 e2 e3</div><br />
Mais mettons d’abord au point les détails d’une position.<br />
<br />
<b>Manipuler des bits</b><br />
Haskell propose un système de modules assez complet. Pour en effleurer la surface, je précise seulement que vous devrez ajouter « import Data.Bits » au début de votre programme, ou, dans l’interpréteur, indiquer « :module Data.Bits », pour utiliser certaines des fonctions employées dans ces bouts de code.<br />
<br />
Comme la position d’un joueur est encodée sur 9 bits, il nous faut un masque pour cacher le reste :<br />
<br />
<div style="margin-left:40px">mask9 :: Int  <font color="#8B4513"> --les valeurs entières sont par défaut des Integer (infinis), préciser donc le type Int (32/64 bits) pour une optimisation sans effort</font><br />
mask9 = 2^9-1    <font color="#8B4513">-- #111111111b ( ! ce n’est pas un littéral Haskell ; pas hélas de représentation binaire native)</font><br />
 <br />
turnBit :: Int   <font color="#8B4513">--pour donner un nom au bit qui détermine de qui c’est le tour.</font><br />
turnBit = 18<br />
<br />
allMoves :: [Int]  <font color="#8B4513"> --tous les mouvements possibles : une compréhension de liste qui sera mémoïsée après sa première utilisation</font><br />
allMoves = [2^x | x &lt;- [0..8]]</div><br />
<b>Bit I/O</b><br />
Pour simplifier l’écriture des positions, nous écrivons deux petites fonctions pour faire nos opérations de lecture et d’écriture dans les positions encodées dans un entier : une pour lire (decomp), une pour écrire (rebuild).<br />
<br />
<div style="margin-left:40px">rebuild :: Int -&gt; Int -&gt; Int -&gt; Int  <font color="#8B4513">--trois entiers pour en faire un seul : à qui de jouer, la position du joueur, la position de l’ordinateur</font><br />
rebuild t c p = p .|. (shiftL c 9) .|. (shiftL t turnBit)   <font color="#8B4513">-- shiftL x n : déplace n fois à gauche les bits de x  / .|. est le « ou » binaire</font></div><br />
<i>NB : (x, y, z) est un « tuple » à trois éléments : contrairement aux listes, ils peuvent être hétérogènes.</i><br />
<br />
<div style="margin-left:40px">decomp :: Int -&gt; (Int, Int, Int) <font color="#8B4513">--l’opération inverse</font><br />
decomp p = (if testBit p 18 then 1 else 0, (shiftR p 9) .&amp;. mask9, p .&amp;. mask9)</div><br />
<b>L’élégance fonctionnelle</b><br />
Grâce au pattern matching et à la composition de fonctions, nous pouvons écrire une fonction dense et courte (je vous encourage à lire les billets précédents si certains éléments vous manquent pour la compréhension) pour générer les possibles positions suivantes :<br />
<br />
<div style="margin-left:40px"><br />
nextPositions :: (Int, Int, Int) -&gt; [Int]<br />
nextPositions (1, c, p) = map (\x -&gt; rebuild 0 x p) . map (.|. c) . filter (\m -&gt; (m .&amp;. (p .|. c)) == 0)  $ allMoves<br />
nextPositions (0, c, p) = map (rebuild 1 c) .  map (.|. p) . filter (\m -&gt; (m .&amp;. (p .|. c)) == 0) $ allMoves</div><br />
nextPositions utilise le pattern matching pour récupérer de façon élégante les informations contenues dans le tuple généré par decomp :<br />
<div style="margin-left:40px">nextPositions . decomp $ 0  <font color="#8B4513">--génère les positions possibles après la position initiale (tout à 0, c’est le tour du joueur)</font></div><br />
<b>L’infini est un outil bien pratique</b><br />
Il nous faut tout de même une structure hôte, l’ arbre, pour placer les positions générées. Haskell permet de définir de nouveaux types grâce au mot-clé data. La syntaxe (dans sa forme la plus simple –il faudra vraiment que je revienne sur le système de type de Haskell dans un autre billet) est :<br />
<br />
<div style="margin-left:40px">data NomDeType = constructeur arg1 arg2 … argN</div><br />
Dans notre cas, nous l’avons dit, chaque nœud contient une position et ses enfants (qui sont aussi des arbres de jeux ; c’est une structure récursive, encore !) :<br />
<br />
<div style="margin-left:40px">data GameTree = Node Int [GameTree]</div><br />
Il nous suffit donc, pour générer l’arbre de jeux, d’appliquer le constructeur de GameTrees aux positions suivantes de celle dont on part :<br />
<br />
<div style="margin-left:40px">buildGTree :: Int -&gt; GameTree<br />
buildGTree p = Node p (map buildGTree (nextPositions . decomp $ p))</div><br />
Si j’écris : <br />
<br />
<div style="margin-left:40px">buildGTree 0</div><br />
j’obtiens l’arbre de jeu entier des morpions –mais je pourrais l’utiliser pour un jeu où les possibilités sont infinies, grâce à l’évaluation paresseuse ! N’en sera évalué que ce je demanderai…<br />
<br />
<b>Une fonction pour dompter l’infini</b><br />
Il y aurait quand même un certain danger à lancer l’évaluation sur l’arbre infini. Après tout, même pour un jeu de morpions, il y a 9 ! = 362880 parties possibles. Alors imaginez qu’on réutilise notre squelette pour un jeu d’échec !<br />
<br />
Il nous faut donc une fonction équivalente à take (cf <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b409/paresse/" target="_blank">billet précédent</a>) qui fonctionne pour les arbres :<br />
<br />
<div style="margin-left:40px">prune :: Int -&gt; GameTree -&gt; GameTree<br />
prune 0 (Node t _) = Node t []<br />
prune n (Node t rest) = Node t (map (prune (n-1)) rest)</div><br />
<div style="margin-left:40px">prune n gt  <font color="#8B4513">--donne les n premiers niveaux de l’arbre</font></div><br />
<b>Pour être tranquilles avant le prochain billet</b><br />
Dans le prochain billet, nous mettrons en place l’A.I. à proprement parler, sous la forme de l’algorithme <a href="http://fr.wikipedia.org/wiki/Algorithme_minimax" target="_blank">minimax</a>, avec le raffinement <a href="http://fr.wikipedia.org/wiki/%C3%89lagage_alpha-beta" target="_blank">alpha-bêta</a>. Pour pouvoir nous concentrer là-dessus, terminons ce billet par l’écriture d’une fonction d’évaluation statique : elle détermine le score sans regarder les positions suivantes. Elle est simple : elle renvoie 1 si l’ordinateur gagne, -1 si c’est le joueur et 0 sinon. <br />
<br />
Pour déterminer si les positions sont gagnantes, un peu d’infrastructure est nécessaire :<br />
<br />
<div style="margin-left:40px">makeMask :: [Int] -&gt; Int  <font color="#8B4513">--construit un masque à partir de la liste des bits allumés</font><br />
makeMask = foldr ((+) . (2^)) 0 <font color="#8B4513">--encore un pliage de liste ! ((+) . (2^)) &lt; = &gt; \x y -&gt; 2^x + y</font><br />
<br />
<font color="#8B4513">-- la liste des bits allumés dans les positions gagnantes</font><br />
winners’ = [[0,1,2], [3,4,5], [6,7,8], <font color="#8B4513">-- lignes</font><br />
<div style="margin-left:40px"><div style="margin-left:40px">[0,3,6], [1,4,7], [2,5,8], <font color="#8B4513">-- colonnes</font><br />
[0,4,8], [2,4,6]]         <font color="#8B4513"> -- diagonales</font></div></div><br />
winners = [makeMask onBits | onBits &lt;- winners’]  <font color="#8B4513">--on met le tout dans une structure pour que le calcul soit mémoïsé</font></div><br />
Il suffit ensuite de comparer avec la position des deux joueurs :<br />
<i><br />
NB : les pipes ( | )  dans la fonctions sont des « gardes ». Elles correspondent à un switch dans un autre langage : si la condition qui les suit est vraie, la fonction retourne l’expression qui suit cette condition.<br />
La syntaxe est :<br />
NomFonction args<br />
	<div style="margin-left:40px">| condition = expression<br />
	…<br />
	| otherwise = expression    --otherwise &lt; = &gt; par défaut</div></i><br />
<br />
<div style="margin-left:40px">static :: (Int, Int, Int) -&gt; Int   <font color="#8B4513">--à employer avec decomp</font><br />
static (_, p, c)<br />
	<div style="margin-left:40px">| any (\x -&gt; x == x .&amp;. p) winners = -1  <font color="#8B4513">-- any f lst est vraie si f est vraie pour au moins un élément de lst /  .&amp;. = binary and</font><br />
	| any (\x -&gt; x == x .&amp;. c) winners  = 1<br />
	| otherwise = 0</div></div><br />
Et voilà ! en quelques lignes de code (dont une bonne part aurait pu / dû être généralisée – un arbre est une structure bien pratique et très commune, après tout), nous avons déjà fait l’essentiel du travail. Dans le prochain billet, l’A.I proprement dite et quelques réflexions sur la modularité particulière des programmes fonctionnels !<br />
<br />
<u>Exercices</u> :<br />
<div style="margin-left:40px">- représentez le plateau d’un jeu de dames<br />
- quelle fonction d’évaluation statique pourrait être utilisée pour le jeu de dames ?<br />
- lisez l’article dont le billet est inspiré, qui donne d’autres exemples intéressants d’implémentations modulaires pour différents algorithmes<br />
- pour les bons en maths : si une position gagnante ne conduit à la génération d’aucune position nouvelle, de combien de positions l’arbre de jeu complet sera-t-il constitué ?</div></blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b426/19-bits/</guid>
		</item>
		<item>
			<title>Paresse</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b409/paresse/</link>
			<pubDate>Wed, 01 Apr 2015 12:35:10 GMT</pubDate>
			<description>*Les joies de la...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><b>Les joies de la procrastination</b><br />
Je voudrais introduire ici l’évaluation paresseuse (« lazy evaluation ») caractéristique de certains langages fonctionnels, au premier rang desquels Haskell, qui faisait l’objet des deux précédents billets (<a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b394/petite-cuillere-haskell/" target="_blank">1</a>  <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b405/origami/" target="_blank">2</a>). Elle consiste, en deux mots, à reporter l’évaluation d’une valeur au moment où elle est devenue nécessaire. <br />
<br />
L’évaluation paresseuse n’est pas l’apanage des langages fonctionnels –bon nombre d’entre eux reposent  d’ailleurs au contraire sur le principe de l’évaluation stricte, ou immédiate. Inversement, elle peut apparaître dans des langages impératifs : une <a href="http://arma.sourceforge.net/" target="_blank">bibliothèque de traitement des matrices</a>, écrite en C++, en fait d’ailleurs usage, comme du  « <a href="http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern#Static_polymorphism" target="_blank">polymorphisme statique</a> », pour accroître ses performances. Cependant, l’évaluation paresseuse crée plus de problèmes qu’elle n’en résout lorsque le programme est constitué d’une succession d’états : l’état du programme peut avoir changé entre la définition de la valeur et son utilisation. Les <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b375/premiere-dose/" target="_blank">principes fonctionnels</a> garantissent en revanche que ce ne sera pas le cas.<br />
<br />
<b>Les bœufs et la charrue</b><br />
L’évaluation paresseuse est une généralisation du bon sens. Bon sens caractéristique du programmeur, qui n’écrira jamais :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0">pFile = fopen<span style="color: black;">&#40;</span>file<span style="color: black;">&#41;</span> ;
<span style="color: #0000ff;">bool</span> ok = grosseFonctionDeLaMort<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> ; <span style="color: #808080;">// je vais appeler cette fonction pour rien, peut-&ecirc;tre&#133;</span>
<span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span> pFile &amp;&amp; ok<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> &#133;</pre></td></tr></table></pre>
</div><br />
mais :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code C :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">pFile = fopen<span style="color: black;">&#40;</span>file<span style="color: black;">&#41;</span> ;
<span style="color: #0000ff;">if</span> <span style="color: black;">&#40;</span> pFile &amp;&amp; grosseFonctionDeLaMort<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span> &#133; <span style="color: #808080;">// si pFile n&#146;est pas ouvert, grosseFonction ne sera pas &eacute;valu&eacute;e</span></pre></td></tr></table></pre>
</div><br />
Dans un programme paresseux, les appels de fonction et les valeurs non-triviales (ce n’est donc pas le cas des littéraux, par exemple) sont initialement à l’état de « thunk », c’est-à-dire en attente d’évaluation. Les thunks ne sont évalués qu’au moment où c’est nécessaire. <br />
<br />
<b>What’s the point ?</b><br />
Evidemment, la plupart des valeurs définies seront utilisées, et la plupart des thunks évalués. Prenons l’exemple d’une fonction de tri : il est évident que toutes les valeurs de la liste triée seront évaluées à un moment ou à un autre. Le tri est par nature une opération stricte. <br />
<br />
Inversement, prenons l’exemple d’une fonction qui renverrait les éléments d’indice pair dans une liste :<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:72px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br /></div></td><td valign="top"><pre style="margin: 0">evens <span class="br0">&#91;</span><span class="br0">&#93;</span>  = <span class="br0">&#91;</span><span class="br0">&#93;</span>
evens <span class="br0">&#91;</span>x<span class="br0">&#93;</span> = <span class="br0">&#91;</span>x<span class="br0">&#93;</span>
evens <span class="br0">&#40;</span>x:<span style="color: #0000ff;">_</span>:xs<span class="br0">&#41;</span> = x <span style="color: #339933;">:</span> evens xs     <span style="color: #808080;">--les &eacute;l&eacute;ments impairs ne seront pas &eacute;valu&eacute;s</span></pre></td></tr></table></pre>
</div><br />
<b>Vers l’infini et au-delà</b><br />
Une conséquence particulièrement intéressante de l’évaluation paresseuse est la possibilité de travailler sur des structures infinies –les arbres et les listes, par exemple.<br />
<br />
La liste des entiers à partir de 1 peut être définie tout simplement :<br />
<br />
<div style="margin-left:40px">posIntegers = [1..]   <font color="#8B4513"> -- les entiers de 1 à 9 pourraient être désignés par [1..9]</font></div><br />
Au-delà de ce sucre syntaxique, voici une fonction inifinie :<br />
<br />
<div style="margin-left:40px">fib m n = m : (fib n (m+n))        <font color="#8B4513">--la suite de Fibonacci</font></div><br />
Comment utiliser ces listes ou ces fonctions infinies ? Elles peuvent servir en argument d’une fonction qui définit la portion qu’elle veut utiliser. La fonction la plus simple est take :<br />
<br />
<div style="margin-left:40px">take 0 _ = []<br />
take n (x : xs) = x : (take (n-1) xs)<br />
<br />
<font color="#800080">&gt;</font> take 5 $ fib 1 1<br />
<font color="#0000FF">[1,1,2,3,5]</font></div><br />
<b>Mémoire de l’infini</b><br />
Autant une valeur, une fois évaluée, pourra être réutilisée sans nouvelle évaluation (le thunk a été remplacé par une valeur à l’adresse pointée par le nom de la variable), autant chaque appel de fonction aboutira à la création d’un nouveau thunk. L’application d’une technique de « <a href="http://fr.wikipedia.org/wiki/M%C3%A9mo%C3%AFsation" target="_blank">mémoïsation </a>» est possible, mais à la charge du programmeur.<br />
<br />
Cependant, les structures de données nommées bénéficient automatiquement de cette technique. Les éléments évalués d’une liste infinie, définie par une fonction par exemple, resteront en mémoire.<br />
<br />
<b>List comprehension</b><br />
Les « list comprehensions » dont la traduction littérale, « compréhension de liste », est un peu ambiguë, mettent l’accent sur cette possibilité de mémoïsation. Voici quelques exemples de leur syntaxe :<br />
<br />
<div style="margin-left:40px"><font color="#8B4513">-- [ fonction | arg &lt;- source ]</font><br />
puissancesDe2 = [2^x | x &lt;- [1..]]<br />
<br />
<font color="#8B4513">-- [fonction | arg1 &lt;- source1 , arg2 &lt;- source2]<br />
-- lorsqu’il y a plusieurs sources, fonction est appliquée à leur produit cartésien</font><br />
ProdCart m n  = [ [x, y] | x &lt;- [1..m], y &lt;- [1..n]]<br />
<br />
<font color="#800080">&gt;</font> ProdCart 4 4 <br />
<font color="#0000FF">[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]]</font></div><br />
<b>Une petite torsion de cerveau pour finir</b><br />
L’évaluation paresseuse permet de créer des compréhensions de listes infinies par récursion. Je vous laisse méditer cet exemple :<br />
<div style="margin-left:40px"><br />
fib = 1:1:[x | x &lt;- zipWith (+) fib (tail fib)]   <font color="#8B4513">-- zipWith f a b crée une liste résultant de l’application de f à chaque élément de a et de b</font></div><br />
<u>Exercices :</u><br />
<div style="margin-left:40px">- rédigez une compréhension de liste définie comme l’ensemble des nombres premiers<br />
- voyez-vous les avantages pratiques à travailler avec des structures inifinies ? expliquez pourquoi<br />
- pourriez-vous implémenter l’évaluation paresseuse dans votre langage favori ? à quel prix ? qu’est-ce que cela dit de l’équivalence de Turing ?</div></blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b409/paresse/</guid>
		</item>
		<item>
			<title>Origami</title>
			<link>https://www.developpez.net/forums/blogs/643915-stendhal666/b405/origami/</link>
			<pubDate>Tue, 31 Mar 2015 10:06:11 GMT</pubDate>
			<description>*L’imagination au pouvoir*...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><b>L’imagination au pouvoir</b><br />
L’Origami, art du pliage, permet de transformer une simple feuille en une grue, un canard ou un brontosaure –art poétique, donc, qui fait émerger de la virginité d’une page un monstre depuis longtemps disparu ou le vol d’un oiseau. Peut-être que notre activité –à nous autres programmeurs- n’a pas pour le grand public l’attrait oriental de cette discipline épurée et pourtant ! ne consiste-t-elle pas à faire émerger du chaos d’interminables séquences de bits des structures vivantes et disciplinées ?<br />
<br />
<b>L’art du pliage</b><br />
C’est dans cet état d’esprit que je voudrais aujourd’hui illustrer l’art du pliage… des listes. Nous répèterons les gestes constitutifs de cet art comme autant de <a href="http://fr.wikipedia.org/wiki/Kata" target="_blank">katas</a> pour en percevoir l’extrême versatilité.<br />
<br />
Il existe essentiellement deux formes de pliage : de gauche à droite, et de droite à gauche. De gauche à droite, on pose d’abord un pan du tissu sur la table, puis on ramène le tissu, pli par pli. De droite à gauche, on tient le tissu à la main et on l’attire vers soi, pour ne le déposer qu’à la fin sur la table. Pour plier une liste, également, on suit un ordre ou l’autre : il nous faut une fonction qui décrive le pli, une valeur initiale qui tient lieu de table et une liste, enfin, notre tissu. En voici la transcription en code<br />
<br />
<i>N.B : pour les rudiments de Haskell nécessaires à la lecture de ce billet, voir le <a href="http://www.developpez.net/forums/blogs/643915-stendhal666/b394/petite-cuillere-haskell/" target="_blank">précédent</a>.</i><br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code Haskell :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:120px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td valign="top"><pre style="margin: 0"><span style="color: #808080;">-- foldl : fold en anglais, plier en fran&ccedil;ais ; l pour left : de gauche &agrave; droite</span>
<span style="color: #0080ff;">foldl</span> <span style="color: #0000ff;">_</span> i <span class="br0">&#91;</span><span class="br0">&#93;</span> = i      <span style="color: #808080;">--il n&#146;y a plus de morceau &agrave; plier, on donne la liste pli&eacute;e</span>
<span style="color: #0080ff;">foldl</span> f i <span class="br0">&#40;</span>x:xs<span class="br0">&#41;</span> = <span style="color: #0080ff;">foldl</span> f <span class="br0">&#40;</span>f i x<span class="br0">&#41;</span> xs    <span style="color: #808080;">--on replie sur la valeur initiale le prochain pan de la liste</span>
&nbsp;
<span style="color: #808080;">-- foldr : fold right, de droite &agrave; gauche</span>
<span style="color: #0080ff;">foldr</span> <span style="color: #0000ff;">_</span> i <span class="br0">&#91;</span><span class="br0">&#93;</span> = i    <span style="color: #808080;">-- arriv&eacute;s au bout de la liste, nous la posons sur la table</span>
<span style="color: #0080ff;">foldr</span> f i <span class="br0">&#40;</span>x:xs<span class="br0">&#41;</span> = f x <span class="br0">&#40;</span><span style="color: #0080ff;">foldr</span> f i xs<span class="br0">&#41;</span>     <span style="color: #808080;">--nous plions le premier morceau de la liste sur le reste de la liste &agrave; plier</span></pre></td></tr></table></pre>
</div><br />
<b>Quelques figures simples</b><br />
Regardons un peu ce que l’on peut faire avec ces deux gestes simples et des fonctions primitives :<br />
<br />
copy = foldr ( : ) []                           <font color="#8B4513">--le constructeur de liste, qui est un opérateur, est placé entre parenthèse pour être utilisé comme une fonction</font><br />
length = foldr (\x y -&gt; 1+y) 0           <font color="#8B4513">--longueur de la liste</font><br />
remove elem = foldr (\x y -&gt; if x == elem then y else x:y) []        <font color="#8B4513">--retirer un élément de la liste</font><br />
sum = foldr (+) 0                           <font color="#8B4513"> --somme</font><br />
product = foldr (*) 1                       <font color="#8B4513">--produit</font><br />
append a b = foldr ( : ) b a                <font color="#8B4513">--concaténer deux listes</font><br />
map f = foldr (( : ) . f) []                   <font color="#8B4513">--appliquer une fonction à une liste</font><br />
<br />
La direction du pli ne compte pas toujours, mais souvent: Prenez par exemple ce joli bout de code :<br />
<br />
<i>N.B : flip intervertit l’ordre des arguments d’une fonction : flip f = \x y -&gt; f y x</i><br />
<br />
reverse = foldl (flip ( : )) []               <font color="#8B4513">  --inverse l’ordre de la liste</font><br />
<br />
<b>Une figure pour le poker</b><br />
Je vous avais promis une formulation plus élégante de split-if, utilisée pour analyser les mains au poker (fonction développée et utilisée lors des trois précédents billets), la voici, d’un geste de Samouraï :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:84px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br /></div></td><td valign="top"><pre style="margin: 0">--@ permet de nommer la structure déconstruite par pattern matching
splitIf' p xs = foldr (ft p) [[]] xs
<div style="margin-left:40px">where ft _ x [[]] = [[x]] ;
<div style="margin-left:40px">ft p x n@((y:ys):zs) = if p x y then [x] : n else (x:y:ys) : zs</div></div></pre></td></tr></table></pre>
</div>En voici une de disambiguate :<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">disambiguate' a b = foldr (\x y -&gt; if x == EQ then y else x) EQ $ zipWith compare a b       
--les recherches sur compare et zipWith sont laissées au lecteur</pre></td></tr></table></pre>
</div><b>Plier son cerveau</b><br />
Enfin je propose, à ceux qui n’auraient dans la journée que la stimulation intellectuelle de faire des boucles,<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:36px;">for (int i = 0 ; i &lt; MAX_VAL ; ++i)  // si j’avais gagné un euro à chaque fois…</pre>
</div>un petit bout de code qui leur tordra convenablement le cerveau :<br />
<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:36px;">foldl f a bs =   foldr (\b g x -&gt; g (f x b)) id bs a</pre>
</div><b>Pour ceux qui doutent encore…</b><br />
de l’intérêt de tout cela, j’annonce une série de billets prochains sur la mise en place d’une IA simple et modulaire pour des jeux de plateau mettant à profit les katas du jour. Nous rendrons ainsi hommage aux morpions !<br />
Comme il se peut que j’aie quelques notions importantes à présenter d’abord, laissez-moi tout de même un ou deux billets avant de lancer la série.<br />
<br />
<u>Exercices :</u><br />
- réfléchir aux représentations d’un plateau de morpions<br />
- faites une recherche sur l’algorithme minimax<br />
- êtes-vous plutôt strict ou paresseux ? si vous êtes plutôt paresseux, je montrerai dans le prochain billet tous les bénéfices de ce trait de caractère…</blockquote>

]]></content:encoded>
			<dc:creator>stendhal666</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/643915-stendhal666/b405/origami/</guid>
		</item>
	</channel>
</rss>
