En effet, grâce à cette dernière, l'ordinateur ne voit plus les choses uniquement en noir et blanc, mais en niveau de gris.

En clair, lorsque la logique floue est utilisée, une réponse à une question ne sera pas forcément un 1 ou un 0 , mais un nombre compris entre 0 et 1.

Septique ? Cela peut se comprendre, et pourtant, les utilisateurs de Git, par exemple, voit régulièrement, surtout s'il tape aussi mal que moi, les effets de la logique floue.

En effet, Git, lorsqu'il ne reconnait pas l'un des arguments qui lui est passé, a le bon goût de proposer des alternatives possibles et surtout crédibles.

Je me suis d'ailleurs toujours demandé la raison pour laquelle cela a été implémenté dans Git vu son ergonomie exécrable, mais c'est peut être l'illustration parfaite du fait que le besoin crée la fonctionnalité, et de toute façon, ce n'est pas la question.

Ainsi, si par maladresse, vous tapez par exemple satus au lieu de status, Git vous proposera des alternatives :

fch@Storm:~/Atoum(master *)
165> git satus
git: 'satus' is not a git command. See 'git --help'.
Did you mean this?
status

L'intérêt est évident, et j'ai donc décidé d'implémenter la même chose dans atoum, mon framework de tests unitaires pour PHP 5.3, d'autant plus que j'ai eu quelques rapports de bug m'informant que l'argument --testIt, qui permet d'exécuter simplement les tests unitaires de atoum, ne fonctionnait pas.

Or, à chaque fois, les utilisateurs avaient tapé --testit au lieu de --testIt.

Il existe plusieurs fonctions permettant de faire de la logique floue en PHP dans ce contexte, tel que soundex(), metaphone() et levenshtein(), qui donne son titre à ce billet.

Les fonctions soundex() et metaphone() permettent d'obtenir, sous la forme d'une chaîne de caractères, ce que j'appelerais une empreinte sonore de la chaîne de caractères qu'elles reçoivent en argument.

En effet, à la différence de md5() ou sha1() qui calcule l'empreinte d'une chaîne de caractères à partir des symboles qui la compose, soundex() et metaphone()calculent l'empreinte à partir de la façon dont la chaîne se prononce.

Ainsi, si les empreintes sha-1 des chaînes toto et ToTo seront différentes, leurs empreintes sonores seront identiques puisque ces deux chaînes se prononcent de la même manière.

De même, si les empreintes sha-1 des chaînes status et statu seront très différentes, leurs empreintes sonores seront par contre très proche.

C'est d'ailleurs la raison pour laquelle nous allons utiliser la fonction levenshtein(), puisqu'elle permet de calculer une distance entre deux chaînes de caratères.

Deux chaînes identiques auront ainsi une distance de 0, tandis que deux chaînes différentes auront une distance plus ou moins grande en fonction de leur degré de similitude.

Concrètement, il suffit donc de calculer l'empreinte sonore de l'argument saisi par l'utilisateur et qui n'a pas été reconnu comme valide, puis de calculer la distance de levenshtein de cette empreinte avec l'empreinte sonore de chaque argument possible.

Il y a en effet de fortes chances que l'argument ayant la proximité la plus proche avec l'argument saisi par l'utilisateur soit celui que ce dernier a voulu utiliser.

<?php

$availableArguments = array('--directory', '--testIt', '--configuration');
$userArgument = '--direct';

$userMetaphone = metaphone($userArgument);
$minLevenshtein = null;
$closestArgument = null;

foreach ($availableArguments as $availableArgument)
{
   $levenshtein = levenshtein(metaphone($availableArgument), $userMetaphone);
   if ($minLevenshtein === null || $levenshtein < $minLevenshtein)
   {
      $minLevenshtein = $levenshtein;
      $closestArgument = $availableArgument;
   }
}

var_dump($closestArgument);

?>

Le code ci-dessous a évidemment été très simplifié, mais l'idée générale telle qu'elle a été implémentée dans atoum est bien présente, ce qui permet d'obtenir le résultat suivant :

fch@Storm:~/Atoum(master *)
167> php scripts/runner.php --direcdory tests/classes
Error: Argument '--direcdory' is unknown, did you mean '--directories' ?

Évidemment, ce mécanisme a ses limites, et si l'utilisateur fait vraiment n'importe quoi, il ne permettra pas de corriger le tir.

Cependant, dans la très grande majorité des cas, il donne d'excellent résultat et permet d'améliorer l'expérience utilisateur de manière significative.

De plus, cette technique a deux inconvénients dont il faut tenir compte afin d'en tirer le meilleur parti.

Tout d'abord, les fonctions soundex() et metaphone() sont particulièrement adaptées à la langue anglaise, et il faudra donc se tourner vers des variantes des algorithmes sous-jacents pour gérer efficacement d'autres langages.

Ensuite, ces fonctions sont relativement gourmandes en ressource, et il est donc impératif de prendre cela en compte leur de leur mise en œuvre, sous peine d'obtenir des mauvaises performances en fonction du volume et de la nature des information à traiter.