samedi 26 mai 2012

Utilisation de Proc#curry

Avant propos
On m'a souvent fait remarqué que je ne faisait rien dans et pour Funkywork alors j'ai décidé d'écrire un micro-article qui tire partit d'une petite astuce présentée précédemment.
Sachez tout d'abord que cet article n'a pas pour objectif de présenter un fait, "une vérité absolue", mais une manière que je considère comme élégante (et potentiellement amusante) de résoudre certains problèmes.

Contenu de l'article
Ce billet présentera une manière d'utiliser la méthode Curry de la classe Proc, car comme Pierre en avait parlé dans son article, on en voit rarement l'intérêt (cf : L'article en question ).
Cette manière n'est certainement pas la seule, ni même la meilleure, mais elle a au moins le mérite d'être originale.

Contextualisation
Pour un projet "divers", j'ai du manipuler des projectiles. Il existe plusieurs manière de déplacer un projectile, j'ai choisis d'utiliser une fonction.
L'équation d'une fonction linéaire est définie comme ceci : f(x) = ax + b (sans rentrer dans les détailles et les informations de précisions).
L'objectif de ma fonction et de trouver la valeur de "y" pour une fonction définie par 2 points.
Pour cela, rien de bien compliqué, il suffit de trouver "a" et "b" avec les données qu'on nous donne. Soit :

  • a = (source_y-arrivee_y)/(source_x-arrivee_x)
  • b = source_y - source_x*a
Ce calcul n'est évidemment pas très compliqué à réaliser (et à trouver), cependant, je voulais ne pas avoir a répéter mes données source_x/y et arrivee_x/y à chaque utilisation.
J'ai donc choisi de retourner une fonction anonyme.

Implémentation
Pour avoir une fonction facilement utilisable, j'ai décidé d'utiliser les fonctions d'ordres supérieurs, soit des fonctions qui peuvent prendre des fonctions en argument et/ou retourner des fonctions. 
Un exemple d'utilisation serait:
  1. line_proc = equation_line(1,2,2,21)
  2. (0..150).each do | x |
  3.         put "(#{x}, #{line_proc.(x)}"
  4. end
(Afficher toutes les coordonnées de 0 à 15 pour une fonction linéaire passant de (1,2) à (2, 21))

Comment procéder (en utilisant la Curryfication) ? L'idée de la fonction est donc de retourner un Proc/une Lambda qui appliquerait la fonction trouvée (en calculant "a" et "b") à une valeur d'entrée.

Un problème de portée?
Je ne suis pas un spécialiste du Ruby et certains gourous pourront me contredire, mais j'ai vraiment du mal à percevoir la portée des variables, principalement dans le cas des "blocks". 
Par exemple : 
  1. a, b, c = 1, 2, 3
  2. l  = lambda{|x|a + b + c + x}
Est-ce qu'on peut utiliser a, b et c comme des variables globales (dans le cas présent) ?
Même si cet exemple est admissible, je trouve (et ce n'est que mon avis), que ce n'est pas très "joli".

Une autre idée aurait été de calculer les valeurs "a" et "b" dans la fonction que je retourne, le soucis c'est qu'un calcul qui ne devrait être exécuté qu'une seule fois devra être, pour chaque "y" demandé, recalculé. 
C'est pourquoi, la conclusion qui s'est naturellement offerte a moi est : "Passons "a" et "b" en argument !".
Le problème c'est qu'en dehors de ma fonction, je ne connais pas "a" et "b". 
La curryfication nous permettra donc de retourner une lambda a qui nous avons déjà passé 2 arguments, en l'occurrence "a" et "b". Voici une petite implémentation.
  1. def line_equation(sx, sy, cx, cy)
  2.        a = (sy-cy)/(sx-cx)
  3.        b = (sy - sx*a)
  4.        fun = lambda{|ap, bp, x|ap*x + bp}
  5.       return fun.curry.(a, b)

Conclusion
Je suis parfaitement d'accord pour dire que l'utilité de cet article est fortement discutable, cependant, je trouvais ça amusant de présenter une utilisation concrète de cette méthode.
De plus, elle permet d'être très fiable quant aux règles de portées de variables (domaine Ô combien flou pour les êtres limités comme moi dans la programmation) et aussi parce que c'est rigolo d'avoir du curry dans son code.

Merci de votre lecture. 
Raho.

mardi 22 mai 2012

Le Monkey Patching


Cet article est disponible en PDF


Résumé
Dans cet article, je vais tâcher de présenter une pratique de programmation douteuse appelée le « Monkey Patching », ou la « modification du singe » (aussi appelée le « Guérilla Patching ». Ainsi que de développer son intérêt dans la rédaction de scripts pour le logiciel RPG Maker. Sachez tout d'abord que je ne suis pas un grand adepte de ce genre de pratique, mais que dans les exemples présentés dans ce papier, le Monkey Patching me semble être une solution intéressante.



Introduction
Le terme Monkey Patching désigne la modification/l'extension de code source sans altérer la source originale (principalement dans les langages de programmations dynamiques). On peut considérer ça comme une extension de classe déjà déclarée (dans le cadre d'un langage orienté objets).
Concrètement, le Monkey Patching me permet d'ajouter/de greffer, de modifier des éléments d'une classe sans modifier le code original. Par exemple, je déclare une classe Voiture et un peu après, je lui ajoute une méthode rouler. Un autre exemple serait d'ajouter une méthode getFirst à ma classe String qui me retournerait le premier caractère d'une chaîne de texte. Certains langages permettent ce genre de pratique, je citerai non-exhaustivement Ruby, JavaScript ou encore Python. Cet article portera principalement sur Ruby.



Opinion sur le Monkey Patching
Bien que cela puisse paraître assez alléchant, le Monkey Patching pose énormément de problème de « raisonnement », en effet, nous sommes généralement habitués à lire notre code de manière linéaire, en abusant du Monkey Patching, lire un code (et donc, par extension, le raisonner), devient beaucoup plus complexe car nous ne savons jamais si le code que nous lisons est la « version finale ». Peut être qu'un patch a été rédigé plus loin, altérant un comportement défini. Donc, dans un projet classique, l'abus de ce genre de pratique peut entraîner une perte de temps conséquente dans la relecture du code.


Exception, les potentiels bienfaits du Monkey Patching
Dans certains cas, le Monkey Patching peut être une bénédiction. Comme exemple, je vais citer les logiciels RPG Maker édités par Enterbrain qui offrent (depuis leur version XP) une solution de programmation embarquée (utilisant le langage Ruby).


Un outil destiné aux non-programmeurs
RPG Maker est un jeu pour faire des jeux, dans ce sens, il n'est pas demandé à l'utilisateur de maîtriser le langage Ruby pou réaliser son jeu. La programmation n'est qu'un atout complémentaire pour pousser la personnalisation un peu plus loin. C'est donc naturellement qu'une nouvelle classe d'utilisateurs est née, les programmeurs, qui sans pour autant se lancer dans la création de jeux complets, produisent des patchs pour le logiciel en lui greffant/ en modifiant de nouvelles composantes.

Sans le Monkey Patching, l'ajout de ces patchs auraient demandés à l'utilisateur, une connaissance de Ruby (assez sommaire cela dit) et de modifier le code original du jeu. Grâce au Monkey Patching, il est possible d'ajouter ces patchs les uns à la suite des autres assurant une certaine compatibilité (dans la mesure du
possible).


Pourquoi Monkey Patcher plutôt que modifier le code original
Voici une petite liste des intérêts du Monkey Patching (dans le cadre de RPG Maker) non-exhaustive.

  • Possibilité de revenir au code d'origine facilement
  • Au moyen d'alias, assurer une compatibilité sensible
  • Réduire le nombre de modifications
  • Dans le cas de la lecture d'un script, comprendre ses modifications.

Je pense tout de même que le principal intérêt est de pouvoir facilement restaurer le code d'origine et de maximiser les inter-compatibilités.


Les alias, une des armes du Monkey Patching
Nous avons vu que nous pouvions « écraser » des méthodes, des attributs, mais les alias nous permettent de changer le nom d'une méthode. Cela peut être très pratique dans le cas ou je dois, par exemple, modifier une méthode pour lui greffer des actions. Je n'ai qu'a créer un alias de la méthode, réécrire la signature de la méthode, dans ma nouvelle méthode j'appelle l'alias et à la suite, j'écris mes modifications.
Malgré le côté très naïf de la chose, c'est un maximisant les alias faisables qu'on peut augmenter la compatibilité. Par exemple, admettons que j'écrive un patch pour ajouter à ma classe Game_Party un compteur de combat remportés. La solution « triviale » serait de modifier directement la classe. Mais admettons qu'un autre patch sorte pour ajouter une gestion des quêtes, si les 2 programmeurs n'utilisent pas les alias, les modifications d'un patch écraseront les modifications de l'autre. Alors que si l'un alias le constructeur de Game_Party, l'appelle dans son nouveau constructeur puis ajoute sa modification et que le suivant alias (une fois de plus) le constructeur de Game_Party l'appelle, et fait ses modifications, les 2 seront prises en comptes.


Conclusion
Je n'ai volontairement pas mis d'exemple de code car il s'agit plus d'exemples théoriques. Je maintiens que le Monkey Patching rend la lecture du code compliqué, mais dans le cas de patchs pour RPG Maker (par exemple) ils peuvent garantir une bonne compatibilité et une possibilité de revenir en arrière facilement !

Liens complémentaires (et probables sources)

Merci de votre lecture,
Michaël Spawn