lundi 28 janvier 2008

MetaTricks : Présentation DSL en Ruby

Une présentation très intéressante de la métaprogrammation, et de sa mise en pratique pour la conception de langages dédiés (DSL en anglais), lors du Belgian Ruby User Group meeting du 3 Octobre 2007.

  • Vidéo et sources du DSL présenté : chez XAOP
  • Slides de la présentation en PDF (recommendés pour suivre la vidéo) : sur RubyForge
XAOP est une société informatique Belge utilisant entre autre Ruby.

Dans un premier temps, la présentation revient sur la définition de la métaprogrammation et d'un DSL (en insistant sur le fait que cette notion dépend fortement de l'angle par lequel on regarde un langage donné). Est ensuite discutée la facilité de métaprogrammation en Ruby (comparé au Java notamment).
La seconde partie concerne plus particulièrement les techniques employées pour la conception de langage dédié (exemple d'une banque) : gestion de self, constantes et méthodes manquantes et personnalisation des opérateurs notamment (et petites allusions à Rails).

vendredi 25 janvier 2008

Conversion ASCII en Ruby

Petit retour sur la conversion en code ASCII en Ruby (cf. précédent) pour avoir la conversion dans les 2 sens :


# Pour les entiers entre 0 et 255
97.chr
#=> "a"
?a
#=> 97

jeudi 24 janvier 2008

Software Engineer. Engineer ?

Un début de débat intéressant (tendant un peu vers le troll), par blogs interposés, sur la question suivante (et ses corollaires) :

Un "Software Engineer" (approximativement : ingénieur logiciel) est-il un ingénieur à proprement parler ?

D'où découlent plusieurs questions intéressantes, quasiment épistémologiques pour certaines :
  • Peut-on définir l'ingénieur comme étant celui qui utilise les mathématiques comme un outil fondamental ?
  • Qu'est-ce qu'un modèle scientifique ? Pourquoi UML, par exemple, ne semble-t-il pas en être un ?
  • Quelle(s) valeur(s) pour mesurer un "travail" ?
  • Distinctions entre un ingénieur, un chercheur ("scientist" dans les articles) et un cadre ("clerk" ici) ?
Le débat :

mercredi 23 janvier 2008

Un Pong Fluide !

Un peu par hasard, je suis tombé sur un jeu magnifique : Plasma Pong. Tout le monde connaît le Pong. Tout le monde sait y jouer... Mais celui-ci est original, puisqu'il permet de projeter, ou d'absorber du "plasma" (du fluide coloré en fait...) pour propulser la balle. Visuellement, c'est vraiment superbe, et ça renouvelle vraiment le genre. Il y a même un coin "bac-à-sable" pour faire tester le fluide : viscosité, turbulence, pression, densité... C'est pas un logiciel de CFD, mais on a vite des effets très admissibles : c'est vraiment surprenant !



Par contre, je ne l'ai trouvé que pour Windows, désolé... Apparemment, il y a un petit soucis avec Atari, et une prochaine version devrait venir sous un autre nom. La page du projet, par Steve Taylor, est donc actuellement en stand-by.

J'espère qu'il s'agit (s'agira plutôt) d'un programme open-source, je serais curieux de voir comment la partie CFD est implémentée...

lundi 21 janvier 2008

Première "vraie" contribution

J'utilise énormément Wikipedia, en Français comme en Anglais. Je mets souvent des liens en référence sur ce blog vers cette encyclopédie "libre". Lorsque je cherche une définition, c'est assez systématiquement l'outil que j'utilise.

Et pourtant je suis bien conscient de ses limites et défauts. Trop de liberté pour les contributeurs ? Sources parfois/souvent non-vérifiées ? Détournement du média ? Approximations ? Erreurs ? Les critiques sont assez nombreuses sur le web, et parmi le corps enseignant (pour de bonnes et de mauvaises raisons à mon humble avis...).

Le fait est qu'en ce qui concerne les sciences, le niveau est assez correct. Peut-être ce sujet est-il lui même de nature "encyclopédique" et "libre" ? Certains articles, dans d'autres domaines, me semblent d'une très bonne qualité. Quelques perles :


Bref, ça faisait quelques temps déjà que je corrigeais quelques petites choses par-ci par-là (orthographe, traductions, précisions...). Aujourd'hui, j'ai créé mon premier article.
Il s'agit de Martin Knudsen, fluidicien danois ayant étudié, entre autre, les écoulements de gaz.

J'avais utilisé ses travaux il y a quelques temps déjà, dans le cadre d'écoulements gazeux en milieux poreux (joints de robinets industriels pour la pétrochimie et la pharmaco-chimie... pas pour celui du lavabo !). Alors oui, c'est simplement une traduction de la version anglaise, il ne déplacera pas les foules, et la vie de l'intéressé n'est pas exactement passionnante... Mais il n'y avait pas de raison pour qu'il n'apparaisse pas dans Wikipedia en français. Voilà. En tout cas, vu ce que j'utilise Wikipedia, il me semble normal de commencer à m'impliquer d'avantage dans son développement, même à mon modeste niveau.

Soldier in Mission

Je sais que vous êtes nombreux, parmi mes milliers de visiteurs réguliers (bientôt millions !), à exercer un honorable métier dans le domaine des ressources humaines...

Bref, ma mission se terminant incessament sous peu, je suis actuellement à la recherche d'un emploi d'ingénieur en modélisation et développement, idéalement sur codes de calculs ou autres applications scientifiques, sur la ville de Toulouse. Je fournis un CV en .doc ou .pdf sur demande, via le profil. Et je ne bois pas de café.

Qui ne tente rien n'a rien...


Dans l'attente d'une rép... Mais qu'est-ce que je dis moi !!?

jobboards = [:monster, :keljob, :apec, :sudouestjob]
keywords = ["fortran", "calcul", "modélisation",
"CFD", "développement", "éléments finis",
"volumes finis", "fluides"]

jobboards.each{|jb|
jb.offres.each{|offre|
if offre.lieu == "Toulouse" then

keywords.each{|kw|
if offre.include? kw then
offre.catch
end
}
}
}


Avec cette méthode, on tombe malheureusement sur trop peu d'offres de ce genre.

dimanche 20 janvier 2008

Biomimétisme : algorithme de toile d'araignée

Le biomimétisme est une pratique scientifique consistant à imiter, ou à s'inspirer de systèmes naturels, ou vivants. Parmi les exemples de ce domaine, on retrouve entre autres : formes de poissons pour l'aérodynamisme de voitures, ou autres véhicules, transfert de fluides par capillarité comme dans les végétaux, ou encore l'algorithme de colonies de fourmis pour la recherche du plus court chemin dans un graphe.

Ce que je vous propose aujourd'hui, c'est un petit algorithme maison, implémenté en Ruby, pour simuler la localisation d'une proie dans une toile d'araignée.

La nature :

Les araignées se divisent en de très nombreuses espèces qui ont différentes approches pour la chasse, et notamment l'utilisation de leur toile (certaines n'en tissent d'ailleurs pas...). Pas mal d'espèces utilisent leur toile afin de piéger leurs proies. Certaines sont averties que "quelque chose" est dans leur toile de la manière suivante : une fois la toile tissée, l'araignée tisse un ou plusieurs fils entre la toile et sa cachette, et est avertie par les vibrations de ce(s) fil(s) de "liaison".

Le modèle :

Imaginons qu'une araignée possède une très grande toile. Un seul fil de liaison serait probablement insuffisant car même en étant prévenue de la présence d'une proie, elle devrait ensuite la rechercher, "au hasard", dans sa toile. Bien. Prenons la même araignée, mais ayant créé un fil de liaison avec chacun des fils principaux de sa toile. En prenant la suite des fils touchés par sa proie, elle peut en déduire étape par étape sa localisation. C'est ce que ce propose de faire cet algorithme.

L'algorithme :

L'algorithme proposé peut être qualifié de naïf. On considère que l'espace (plan de fait) est décomposé en différentes zones par les fils de la toile. Un fil touché correspond à un signal.

  1. Début
  2. "zones précédentes" = toute la toile
  3. Pour chaque signal
    • "zones signalées" = zones voisines du fil touché
    • "zones potentiellement occupées" = "zones signalées" accessibles depuis "zones précédentes" via le fil touché
    • "zones précédentes" = "zones potentiellement occupées"
  • Fin
Pour une présentation plus "visuelle", j'ai fait un petit slide disponible ici au format pps (je voulais tester SlideShare, mais le site s'entête à rejeter mes slides... dommage...).

Le code :

Le code Ruby de cet algo est disponible sur Pastie.

zone.rb décrit la classe des zones entre les fils (yarns en anglais), et spiderweb.rb la classe de la toile d'araignée, contenant la méthode run_analysis implémentant l'algorithme proposé ci-dessus.
runtest.rb est un cas test on ne peut plus simple de l'algo : c'est le cas illustré dans mon petit slide.
pentacle.rb est un cas-test de toile en forme de pentacle.

realisticweb.rb permet de construire des toiles de forme "réaliste" : cercles concentriques et rayons (diamètres de fait dans mon code - ajout à la classe Spiderweb).
realweb_test.rb est un cas test associé.

Discussion :

L'efficacité de l'algorithme dépend fortement de la forme de la toile. Ce problème doit très probablement être formulable sous forme de problème de graphe, ou problème entier (cherchons son dual ^^). Selon la forme de la toile, il est possible que l'algo ne puisse pas déterminer une zone unique contenant la proie, même si le nombre de signaux tend vers l'infini.

Utilité ?

Éventuellement pour un système de sécurité (comme dans les films à gros budget... les fils de la toiles deviennent alors des "lasers"...). Plus probablement de la localisation pour une petite intelligence artificielle. Ou même la recherche d'une "localisation abstraite" sur un réseau quelconque (ne me demandez pas pourquoi, je pense à FaceBook là : fils = liens friends, signaux = utilisation du fil par une appli FB, zones = ?).

Remarques / Questions ?

N'hésitez pas : il serait incroyable que j'ai été suffisamment clair ou précis !

jeudi 17 janvier 2008

MetaTricks : define_method

Un bon programmeur est un programmeur feignant... Et en Ruby, il existe une façon relativement simple d'éviter de coder : la méthode define_method. De quoi devenir vraiment feignant (donc vraiment bon !) grâce à la métaprogrammation !


L'exemple suivant est un cas simple. Après avoir défini une méthode mult (multiplication) pour la classe des entiers, je souhaite définir les méthodes mult_by_0, ..., mult_by_10 automatiquement. C'est ce qui est fait dans la première boucle grâce à define_method. Cette méthode prend en entrée le nom de la méthode à définir et lui affecte pour implémentation la closure (ici "mult int").
La vérification que les méthodes d'instances correspondantes ont bien été définies (et pas mult_by_11) est assuré par l'impression de la dernière boucle.

Le code :



class Integer
def mult by
self * by
end

(0..10).each{|int|
define_method("mult_by_#{int}") do
mult int
end
}
end

(0..11).each{|int|
boo = (1.respond_to? "mult_by_#{int}".to_sym)? \
"" : "don't "
puts "Integers #{boo}respond to mult_by_#{int}"
}


Où est la sorcellerie ?

mardi 15 janvier 2008

Les Regexp, c'est le mal

Pourquoi ? Pourquoooooooooi ????




Testeur, fiche mémo, regexpothèque et c'est déjà pas mal... (Mais pourquoi !?)

lundi 14 janvier 2008

Programmation poétique - Chapitre IV : Back to School...

"Je t'explique Pierre : au bout de 3 bons points, tu as le droit de choisir une image. Au bout de 3 images, tu gagnes un poster pour ta chambre. Au bout de 3 posters, comme tu as été très sage et studieux, tu as droit à un bisous de ma part !"

"Tu sais quoi frérot ? Au bout de 3 smacks de la maîtresse, tu as droit, au choix, à une nuit avec elle et des menottes, ou de boire un 'sky avec le dirlo, dans son bureau"

Pierre n'a que 8 ans. Il n'est donc pas trop tenté par faire des salop3ries avec son institutrice (qui en plus n'aime que les chats et Derrick...). Par contre, il est bien tenté par un alcoolisme précoce. Il fouille donc ses poches et réfléchit :


Recompenses = [
:bon_point,
:image,
:poster,
:bisous_maitresse,
:whisky_dirlo
]

class Array
def que_des_bons_points?
res = true
self.each{|elt|
res = false if elt != :bon_point
}
res
end

def attrape_valeur val
i = 0
while self[i] != val
i += 1
end
i
end
end

def convertir recompense, cadeaux
liste = cadeaux
while !liste.que_des_bons_points?
brouillon = []
for elt in liste
if recompense.include? elt then
if elt == :bon_point then
brouillon.push elt
else
i = recompense.attrape_valeur elt
3.times{brouillon.push recompense[i-1]}
end
else
puts "Pfff ! #{elt} ne sert a rien !"
end
end
liste = brouillon
end
liste.length
end

puts "Allez, comptons !"

poche = [:poster, :bon_point, :image, :image, :caillou]
deja = convertir Recompenses, poche
puts "J'ai deja #{deja} bons points"

but = [:whisky_dirlo]
total = convertir Recompenses, but
puts "Il m'en reste #{total-deja} a avoir..."
puts "Et je tiens ma cuite !!!"

dimanche 13 janvier 2008

Matrices en Ruby : 2 - Matrices creuses ?

Petit retour sur les matrices. On avait vu dans le post précédent à ce sujet que la classe Matrix n'interdit pas la création de matrices non-rectangulaires ou creuses.

De façon assez optimiste, je pensais que cela pourrait être utile en particulier pour le traitement de calculs avec matrices creuses, technique régulièrement employée en calcul scientifique pour des questions évidentes d'économie de temps d'exécution et de mémoire. En effet, une cellule vide correspondrait simplement à un 0.0, dont le stockage est inutile. Il suffisait de tester pour voir...

... Et la conclusion est qu'il s'agit ici d'avantage d'un bug que d'une fonctionnalité.

Le code suivant met en évidence le type de problème que l'on peut rencontrer avec une matrice creuse, et propose (hormis la méthode print du post précédent) une petite méthode de "sécurisation" de matrice creuse : secu_sparse.

Le code :



require 'Matrix'
#require 'mathn'#Mandatory for determinant
#calculation with rationnals

class Matrix
# Human readable printing
def print
for i in (0...self.row_size)
strg = ""
for j in (0...self.column_size)
strg = strg + " " + (self.[](i,j)).to_s
end
puts strg
end
end
end

tab = [
[2.2,0.1],
[1.0]
]

mat1 = Matrix.rows tab
puts "Sparse matrix = "
mat1.print

#puts mat1.determinant
#=> error : *nil ?

mat2 = Matrix.zero(2)*0.0
puts "Zero matrix = "
mat2.print

#mat3 = mat2 +mat1
#=> error : +nil ?

class Matrix
def secu_sparse
tab = []
for i in (0...self.row_size)
tab.push []
for j in (0...self.column_size)
tab[i].push ((self.[](i,j))? \
self.[](i,j) : 0.0)
end
end
return Matrix.rows tab
end
end

mat3 = mat1.secu_sparse
puts "Unsparsed matrix = "
mat3.print

MetaTricks : accesseurs

Suite à mon petit article de hier, je me lance dans une série d'articles courts, intitulée MetaTricks, présentant des petites astuces ou fonctionnalités liées à l'introspection et la métaprogrammation, avec petits scripts à l'appui.
Aujourd'hui, les accesseurs.

Les instructions attr_reader/writer/accessor :nom_variable_instance, même si elles forment une entorse au principe de l'encapsulation, sont très pratiques (et utilisées), et font appel à la métaprogrammation. Pourquoi ? Parce qu'elles implémentent automatiquement les méthodes d'accès en lecture et/ou en écriture pour une variable d'instance donnée (ces méthodes sont alors nommées : nom_variable et nom_variable=).

La preuve :


class Barbare
def initialize a = 1
@alpha = a
end
end

a = Barbare.new.methods.sort

class Barbare
attr_accessor :alpha
end

b = Barbare.new.methods.sort

puts "Methodes ajoutees par attr_accessor = "
puts b-a
#=> [alpha, alpha=]


Où est la sorcellerie ?

samedi 12 janvier 2008

Métaprogrammation et Sorcellerie

Le nom est effrayant. "Métaprogrammation". Brrrr !

Dans son très bon article, Giles Bowket essaie de mettre en avant, par diverses citations de Rubyistes reconnus, dont _Why et Russ Olsen (interview - auteur de "Design Patterns in Ruby"), le fait que la métaprogrammation n'est pas un concept obscur et arcanique.

Il y a quelques temps, je vous donnais un lien vers un bon petit tutoriel à ce sujet.

De fait, en Ruby, la métaprogrammation est encouragée, souvent transparente et très utile.

Encouragée, car finalement assez simple syntaxiquement (voir exemples ci-dessous), et surtout déjà implémentée !

Transparente. Si si. Un exemple entre mille : les attr_accessor. Un attr_accessor en début de classe implémente automatiquement les méthodes d'accès en lecture et en écriture d'une variable d'instance donnée. Le saviez-vous ? Pour ma part, j'utilisais les attr bien avant que ce soit le cas !

Très utile. Que celui qui n'a jamais tapé "toto.methods.sort" dans irb me jette la première cacahouète nucléaire. Avant d'avoir la moindre idée de ce qu'était la métaprogrammation ou l'introspection, j'utilisais des méthodes associées à ces concepts (redéfinition de méthodes, inspection des méthodes d'un objet, inclusion d'un module ou non dans une classe, respond_to?, etc...).

Un premier exemple :

Pour montrer l'utilité, et défendre la métaprogrammation contre ses accusations de sorcellerie, ce code implémente 2 fonctionnalités sympas :
  • method_added (méthode de classe) réagit ici à la (re)définition de méthodes pour la classe String. Ici par exemple, on souhaite que ma_meth ne soit pas redéfinie ultérieurement, et on soulève donc une exception dans ce cas. Il existe également method_removed se construisant sur le même schéma dans le cas de suppression de méthode (via undef).
  • method_missing est appelée en cas d'appel à une méthode d'instance inexistante. Si ça c'est pas utile... A noter qu'il s'agit cette fois d'une méthode d'instance.



class String
def ma_meth
# Pour x raisons, cette methode ne doit
# pas etre modifiee
puts "C'est pas faux..."
true
end

def self.method_added(sym)
if sym == :ma_meth
raise "Pas de modif autorisee pour ma_meth !"
else
puts "Methode #{sym} (re)definie"
end
end


def method_missing(m, *params)
puts "Utilise des methodes existantes ! TOJO !!!"
end
end

a = String.new "toto"

puts "Methode inexistante"

a.lalalalaleeeeere

puts "Redefinition"
class String
def ma_meth
puts "autre..."
false
end
end
#erreur soulevee

a.ma_meth


Alors, où est la sorcellerie ?

Dans les références présentées en début d'article, il est dit que la métaprogrammation ne doit pas être désignée par une "pratique avancée". Giles Bowkett propose de dire simplement que c'est une "nouvelle technique" plutôt qu'avancée. Modestement, je serais tentée qu'il s'agit simplement d'une fonctionnalité avancée du langage : cela ne signifie nullement qu'elle serait réservée aux programmeurs "avancés", mais plutôt que le langage est "avancé".

On en reparlera...

vendredi 11 janvier 2008

Astuces HTML pour Lefty

Et mémo pour moi...

Remplacer les accolades par des encadrements "inférieur à", "supérieur à" dans les exemples.

  • Abbréviation : {abbr title="National Association for Stock Car Auto Racing"}NASCAR{/abbr} ==> NASCAR

  • Texte barré : {span style="text-decoration: line-through;"}J'aime la salade{/span} ==> J'aime la salade

  • Texte souligné : {span style="text-decoration: underline;"}J'aime la 'iande !{/span} ==> J'aime la 'iande !

  • Petites capitales : {span style="text-decoration: underline;"}Au bûcher !{/span} ==> Au bûcher !
Ok, les petites capitales ont un intérêt limité (hormis quand on veut "crier" sans faire sauter tout les accents). Souligner le texte n'est pas forcément une bonne idée étant donné que le lecteur lambda s'attend dans ce cas à un lien : en général on met plutôt en gras, mais au besoin...

Une liste assez complète des propriétés CSS (partie du HTML dédiée à la mise en forme - pour résumer) pour les autres propriétés :

Liste CSS

Le tutoriel dont cette liste est piquée issue (très bien fait pour les débutants) :


Mais qui c'est Lefty ?

mercredi 9 janvier 2008

Typage statique ou dynamique : la "Groovy Alternative"

Notion de programmation plus complexe qu'il n'y paraît, le typage est souvent la source de trolls (statique vs dynamique). Pour un petit rappel, l'article Wikipedia, ou un excellent "tuto" (plutôt une présentation en fait) sur le SDZ.

Basiquement, l'opposition entre typage statique et dynamique reposent entre autre sur les arguments suivants :
  • Le typage statique est lourd pour le programmeur, mais plus sûr.
  • Le dynamique est beaucoup plus agréable, mais plus "glissant" et peut-être plus difficile à tester.
Je rentre pas dans le débat... Je me pose seulement la question de l'influence du typage sur les performances (vitesse d'exécution) d'un langage : significatif ou non ?

Pour rappel, un petit tableau :

Typage Fort Faible
Statique Ada, Fortran C
Dynamique Ruby, Groovy Javascript

Le coup du typage fort ou faible : c'est assez relatif, je rentre pas là dedans...

Un exemple en Ruby :



# Test typage
def meth_4_hash hashhh
hashhh.each_key{|k|
puts k.to_s
}
end

conan = {
:dieu => "Crom !",
:arme => "Hache de bataille +4"
}

meth_4_hash conan
#meth_4_hash 455
# ==> erreur de type !

def meth_4_hash2 hashhh
if hashhh.is_a? Hash then
hashhh.each_key{|k|
puts k.to_s
}
end
end

meth_4_hash2 conan
meth_4_hash2 456

def autre_meth hashhh
raise "erreur de type" unless hashhh.respond_to? :each_key
hashhh.each_key{|k|
puts k.to_s
}
end

autre_meth conan
autre_meth 42 #error raised !


Il n'y a pas de type en Ruby : que des objets.

La méthode meth_4_hash doit "implicitement" recevoir un hachage, ou du moins un objet possédant la méthode each_key (doit pas y en avoir 50 !). Mais rien ne m'interdit à priori de lui donner autre chose à manger : 455 par exemple. Dans ce cas, le programme lève une exception et s'arrête.

Une façon de rendre son code plus "sûr" est de tester les entrées ou cibles de ses méthodes. Dans meth_4_hash2, le contenu de la méthode ne s'exécute que si l'input est du bon type (est une instance de la bonne classe plus rigoureusement...). Cette façon de "tester" en entrée de fonction permet ici d'éviter une exception, donc l'arrêt du programme...

... Mais ce n'est pas forcément satisfaisant. Si l'on souhaite être plus rigoureux, on se doit de lever soi-même les exceptions en cas d'erreur de type. C'est ce qui est fait dans autre_meth avec raise qui permet de lever l'exception avec un message qui pourrait être plus explicite (ici "erreur de type : un Hash est attendu en entrée" par exemple). A noter que dans mon exemple, cela comporte peu d'intérêt : il faudrait que le test soit placé en amont dans le code (avec une variable pas codé en dur...).

Groovy est un langage dynamique...



alcibiade = 5
println alcibiade.getClass()

alcibiade = [4,5,6]
println alcibiade.getClass()

assert alcibiade.getClass() == java.util.ArrayList


Tout ce qu'il y a de plus dynamique donc...

... mais peut être statique !



Integer alcibiade = 5
println alcibiade.getClass()

alcibiade = [4,5,6] // erreur !
println alcibiade.getClass()


En effet, sur la première ligne, dans un style "Java", on indique à l'interpréteur (ou au compilateur) que le type de alcibiade doit être statique : ne peut être modifié.

Groovyyyyyyyyyy !

J'aime bien cette fonctionnalité : en fait, j'apprécie assez quand un langage a un comportement par défaut connu (ici le typage dynamique), logique (en accord avec sa "philosophie", ses principes ou ses objectifs) et consistant ("ça marche toujours comme ça" : un minimum d'exceptions aux règles), mais qu'il permet aussi, moyennant un effort minimum, de faire autrement.

1 bon point pour Groovy !

mardi 8 janvier 2008

Traductions de quelques "Pratiques de développeur agile"

Vues sur le blog de Juixe, je ne résiste pas à l'envie de traduire les quelques "Pratices of Agile Developer" de son article... (à noter qu'il trouve les premières évidentes et est d'accord avec les secondes).

  • Soignez le mal, pas les symptomes.
  • La meilleure chose pour le développeur n'est pas nécessairement la meilleure chose pour l'utilisateur, et vice versa.
  • La plupart des utilisateurs préfère avoir un bon software aujourd'hui qu'attendre un an pour en avoir un de qualité supérieure.
  • Les itérations et incréments courts (NDT : probablement au niveau des livrables ici) aident le développeur à rester concentré.
  • Les cas-tests unitaires sont un investissement. Investissez intelligemment. Tester des accesseurs ou des méthodes triviales est le plus probablement une perte de temps.
  • S'il y a plusieurs façons de faire, utilisez celle qui sera compréhensible par plus d'un seul développeur.
  • On conçoit une solution logicielle pour l'utilisateur final. Pas pour le développeur d'à côté, ou pire, pour sa base de données.
  • Si l'utilisateur final ne peut pas utiliser votre programme, c'est un bug.
  • Vous pouvez considérer votre code comme une oeuvre d'art, mais personne ne l'exposera au Louvre
  • Mangez des fruits et des légumes avant de programmer.
Commentaires :

La 1.1 est évidente et piquée aux principes de la médecine (et de l'ingénierie, et tout autre domaine un poil rationnel... Du bon sens quoi, comme la plupart).
La 1.3 est moins évidente. Tout dépend de qui l'on appelle "utilisateur", et de sa propre relation avec lui. Par exemple, on pourrait prendre le cas des (nombreuses) entreprises attendant le SP1 de Windows Vista pour migrer vers cet OS.

La 1.4 fait référence à une pratique d'XP consistant à réduire la fréquence de livraison et d'intégration de modifs dans le produit (pratiques de l'xp)
Sur la 1.5, je n'arrive malheureusement plus à retrouver l'interview d'un dev affirmant qu'il concevait le plus souvent ses cas-tests unitaires avant même d'implémenter ses méthodes. En POO, ça peut être effectivement une approche assez saine : basiquement pour chaque méthode, je dois donner ceci et recevoir cela --> on teste au besoin la nature et/ou le contenu de ceci et/ou cela.

La seconde partie (citations avec lesquelles Juixe est en accord...) revient sur plusieurs notions intéressantes :

D'abord, l'importance de l'utilisateur final. C'est pour lui que l'on code, pas pour la machine (Matz développe ce point dans une interview dont on avait parlé). C'est idiot, mais s'en souvenir est capital.

Ensuite, l'importance de la lisibilité d'un code. Pas pour un mini-script système jetable, bien sûr, mais n'importe quel programme susceptible d'être maintenu se doit d'être lisible. Donc entre autres : commenté clairement, indenté, avec une certaine norme d'écriture (si non-imposée par le langage), avec des variables aux noms clairs, etc...

En passant, le coup des fruits et légumes... C'est idiot, hein ? Une bonne hygiène de vie, patati, patata... Pour seul commentaire : il y avait dans la salle machine où j'ai commencé (dans la douleur) l'apprentissage de la programmation, une affiche sympa. On y voyait un homme à la mine décomposée devant sa bécane, un café à la main devant une horloge indiquant une heure avancée de la nuit. Et en texte : "Dude ! It's 4 A.M: Do you REALLY think you gonna find the bug now !? ".

Enfin, la question de l'aspect artistique de la programmation. C'est un vrai sujet, qui compte pas mal pour la communauté Rubyiste d'ailleurs (voir les réalisations de _Why). La citation 2.4 me rappelle ce que j'ai entendu un jour (pendant un séminaire d'un chercheur ou ingénieur hongrois, expert en modélisation mathématique - je crois...) : "Ne tombez pas amoureux de votre modèle. Si on est amoureux de son modèle, on risque d'hésiter à faire certaines choses. Comme le jeter, le déclarer mauvais ou le reprendre en profondeur. Si vraiment un modèle ne marche pas, il faut le détruire et recommencer à zéro. Pas essayer de l'améliorer..." (adaptation approximative selon mes souvenirs). Grosso modo, c'est la même chose pour un code (l'analogie est d'autant plus forte que les modèles mathématiques dont parlait cet éminent savant ont pour vocation directe d'être implémentés...).

Note pour Lefty : un "développeur agile" n'est pas un programmeur capable de coder en équilibre sur son majeur gauche, avec une rose entre les dents le tout sur un skate reposant sur un ballon multicolore : ça, c'est juste un c0uillon !

lundi 7 janvier 2008

Formats en Fortran

En Fortran 90, la gestion des entrées-sorties est assez spartiate. Il est possible d'utiliser le format libre pour écrire ou lire ses données, mais l'emploi des formats est vivement recommandé si l'on veut être précis avec ses valeurs numériques (pour les flottants notamment) ou si l'on souhaite printer des choses d'une manière "fixe" (pas de décalage de colonne pour un tableau, par exemple). Le code ci-dessous illustre la plupart des descripteurs de format disponibles en Fortran (hormis les caractères de contrôle d'impression pour imprimante : "+", "0", "1", ... --> pour votre santé mentale, n'imprimez pas de Fortran !).

Note :

J'ai toujours codé en Fortran 90. Le code et les commentaires suivants sont donc au moins valables pour cette version et les suivantes (95 et 2003). Cependant, je ne peux pas assurer leur validité pour les versions antérieures de Fortran (77 et avant).

Code :



! Gestion des formats en fortran
PROGRAM main
INTEGER :: entier
REAL*8 :: flott
CHARACTER(LEN = 8) :: chaine
LOGICAL :: vamosalaplaya

entier = 4568
flott = 56.456
chaine = "truites"

vamosalaplaya = .TRUE.

! Sans format
PRINT*, "-----Sans format-----"
PRINT*, entier
PRINT*, flott
PRINT*, chaine
PRINT*, vamosalaplaya

! Avec formats
PRINT*, "-----Avec formats-----"
PRINT '(I6)', entier
PRINT '(F6.3)', flott
PRINT '(A7)', chaine
PRINT '(L3)', vamosalaplaya

! Combinaisons
PRINT*, "-----Combinaisons-----"
PRINT '(I4,3X,F5.2)', entier, flott
PRINT '(2(I4,1X,F5.2,2X))', &
entier,flott,&
2*entier,flott-1.5
PRINT '("N = ",I4,/,"x = ",F6.3)',&
entier, flott
PRINT '(T20,I4,T1,A7)',entier, chaine

! Notation exponentielle
PRINT*, "-----Exponentielle-----"
PRINT '(E8.2)', flott
PRINT '(E9.3)', flott
PRINT '(E10.4)', flott

! Erreurs
PRINT*, "-----Erreurs-----"
PRINT '(I3)', entier
!PRINT '(I4)', flott
! --> erreur a l'exe
PRINT '(E6.2)', flott
PRINT '(I4,1X,T3,F6.3)', entier, flott

END PROGRAM main


Résultats :

 -----Sans format-----
4568
56.4560012817383
truites
T
-----Avec formats-----
4568
56.456
truites
T
-----Combinaisons-----
4568 56.46
4568 56.46 9136 54.96
N = 4568
x = 56.456
truites 4568
-----Exponentielle-----
0.56E+02
0.565E+02
0.5646E+02
-----Erreurs-----
***
******
4556.456


Descripteurs :

Un format en fortran se présente entre quotes et parenthèses. Il est utilisable en écriture (comme ici avec PRINT, ou avec WRITE dans un fichier), et en lecture (READ). On utilise "*" pour indiquer un format libre (pas de format en fait !). Attention, il est nécessaire d'utiliser le même format en lecture que celui employé en écriture lors de la relecture de données (sans quoi les castors mutants prendront le contrôle de nos cerveaux). Passons en revue les descripteurs utilisés :
  • Entiers : Iw, avec w le nombre d'emplacements.
  • Réels, notation flottante : Fw.d avec w emplacements et d chiffres décimaux.
  • Réels, notation exponentielle : Ew.d avec w emplacements (penser au "E+" !) et d décimaux pour la mantisse (pas l'exposant, l'autre...).
  • Booléens : Lw avec w emplacements (intérêt d'écrire des booléens ?).
  • Chaîne : Aw
  • Espaces : wX pour w espaces (à noter que le w se met ici avant).
  • Changement de ligne : / ('(/,/)' pour un saut de ligne donc).
  • Facteur de répétition : exemple : '(2(I4,1X))' équivaut à '(I4,1X,I4,1X)'
  • Positionneur de tampon : Tw, avec w la position absolue dans la chaîne.
  • Libellés : entre double-quotes.

Commentaires :

Oui, c'est lourd. Oui, c'est une source intarissable d'erreurs. En particulier le coup du déplacement de tampon (à éviter à mon avis), les flottants et le facteur de répétition en cas d'inattention... Le A pour les chaînes ne prend pas obligatoirement d'argument (w dans notre notation). A noter qu'il est souvent conseillé de laisser le premier caractère d'impression libre (un espace, quoi) pour éviter tout soucis avec une sortie moyennement standard (avec caractère de contrôle notamment : vous noterez que c'est le comportement par défaut en format libre pour les chaînes de caractères).

En cas d'erreur de ma part ou autre, n'hésitez pas à poster.