Dessiner un logo en LOGO, c'est possible ?
Il y a peu, j'ai trouvé par hasard un article évoquant un improbable interpréteur de langage LOGO en ligne (en version française). La nostalgie s'est alors emparée de moi. Il fallait que je redécouvre ce langage que j'avais appris tout gosse sur un vieux Commodore 64. C'était déjà une antiquité à l'époque, mais surtout, c'est probablement mon tout premier contact avec l'informatique.
Et surtout une question se posait : est-ce que toutes les connaissances accumulées depuis lors allaient rendre l'expérience sans intérêt ou au contraire me permettre d'aborder le langage intelligemment ? La réponse est mitigée mais une chose est sûre, l'expérience n'a pas été inintéressante.
Le LOGO
Pour simplifier sans refaire tout l'historique, LOGO est un (très) vieux langage, né en 1967, à vocation pédagogique, de l'époque du tout début de la micro-informatique. Basé sur Lisp, il est surtout connu pour sa célèbre tortue, nom donné au pointeur à partir duquel on peut tracer à l'écran via les diverses instructions du langage.
La syntaxe ressemble à ça :
POUR CARRE
AVANCE 100 TOURNEDROITE 90
AVANCE 100 TOURNEDROITE 90
AVANCE 100 TOURNEDROITE 90
AVANCE 100 TOURNEDROITE 90
FIN
(Ici CARRE est le nom de procédure qui contient les instructions)
Hé oui, pas d'accolades ou de parenthèses pour encadrer les fonctions. Tout est très verbeux, sans surprise en fait.
Pour les passionnés, Wikipédia possède un article très détaillé sur le sujet
Le langage est tout de même assez complet et exploitable, il permet de manipuler, outre les tracés à l'écran, des entrées/sorties et des fichiers.
Dans mon exemple, je me contenterai de manipuler la tortue à l'écran, tout simplement parce que l'interpréteur se limite à ça.
Du beau code, ça veux dire quoi ?
On a surement tous une idée assez personnelle d'un code bien écrit. Et elle varie surement fortement d'une personne à une autre.
Le langage utilisé à aussi un impact selon ses exigences et ses capacités.
Pour prendre un petit exemple en javaScript :
Le code suivant va paraître maladroit et verbeux à beaucoup d'entre vous.
let warningText;
if (open === true || alarm !== 'active') {
warningText = 'Alert';
} else {
warningText = 'OK';
}
Alors que cette version suivante, épurée, va sembler moins lisible à d'autres
const warningText = open || alarm !== 'active' ? 'Alert' : 'OK';
Enfin, peut-être cette version rassemblera-t-elle plus de monde, mais encore faut-il que le langage utilisé permette l'usage de ternaires.
const warning = open || alarm !== 'active';
const warningText = warning ? 'Alert' : 'OK';
Bref, la réponse à la question du "beau code" est complexe.
Chez Marmelab, on a l'habitude de mettre en avant des pratiques et des méthodes d'écriture assez standard et exigeantes. Je vais donc essayer d'écrire mon programme LOGO avec le code le plus propre possible, en respectant à la fois les contraintes du langage, et les usages de Marmelab, à savoir :
- Attacher une attention particulière au nommage des objets, qu'il permette d'en comprendre le sens sans commentaire.
- Utiliser une règle de casse unique (snake_case, camelCase, PascalCase, ...)
- Tout en anglais pour la standardisation et la compréhension du plus grand nombre
- Découper finement les responsabilités aux sein de fonctions ou autres.
- Factoriser les éléments qui peuvent l'être
- Formater le code toujours à l'identique (règles d'espacement, de retour chariot, ... )
Beau code et LOGO, quelles concessions ?
Voilà donc le défi : avec ce langage, pourrai-je vraiment écrire quelque chose de plus lisible que je ne l'ai fait étant enfant ?
La casse
Premier constat, l'interpréteur utilisé (comme la plupart des interpréteurs LOGO) ne tient aucun compte de la casse.
someThing
, SOMETHING
, sOmEThIng
sont une seule et même référence.
L'usage voudrait qu'un programme LOGO s'écrive en majuscules.
Mais cette pratique n'a plus trop lieu en programmation. Elle rend difficile l'utilisation de variables aux noms élaborés (SQUAREINDEXY
).
Je vais donc préférer le camelCase puisque rien ne s'y oppose (squareIndexY
).
Le programme aura l'avantage de se faire passer discrètement pour du code moderne ;)
La langue
La règle de l'usage universel de l'anglais va devoir être sacrifiée. J'ai choisi bêtement un interpréteur en français, et il ne comprend pas l'anglais. Le code sera donc entièrement en français, pour rester cohérent.
C'est un mauvais point, mais pas irrémédiable. Il existe aussi des interpréteurs LOGO en ligne en Anglais que j'ai trouvés par la suite.
Le formatage
Le formatage du code est un point important. En LOGO toutes les instructions peuvent être combinées sur une seule ligne sans séparateur Et on peut rapidement avoir quelque chose de parfaitement illisible et non maintenable
AVANCE 100 TOURNEDROITE 90 AVANCE 100 TOURNEDROITE 90 AVANCE 100 TOURNEDROITE 90
Une règle simple s'impose : une instruction par ligne
AVANCE 100
TOURNEDROITE 90
AVANCE 100
TOURNEDROITE 90
AVANCE 100
TOURNEDROITE 90
En JavaScript, personne de sensé n'irait écrire :
let name;name = 'John';let isJohn;isJohn = name === 'John';
(même si cela fonctionne)
Pas de linter disponible pour LOGO visiblement, je vais appliquer les règles manuellement sans contrôle automatique. Mauvais point également, mais je doute que prendre le temps d'écrire un linter et des règles de linting pour LOGO soit du temps bien rentabilisé.
La structuration et factorisation
De ce point de vue, LOGO semble posséder déjà toutes les structures basiques nécessaires.
Des variables (en utilisant la syntaxe étrange du caractère " ) :
donne "coteCarre
Des conditions :
si (:erreur) [
avance 100
tourneDroite 90
]
Des boucles :
repete 4 [
avance 100
tourneDroite 90
]
Des fonctions avec paramètres :
pour carre :cote
repete 4 [
avance :cote
tourneDroite 90
]
]
Seul point noir ici, la séparation en fichiers n'est pas possible. Tout le code va donc devoir cohabiter dans le même unique fichier. Pour structurer les éléments, j'utiliserai des commentaires qui compenseront ce manque.
; Definition des constantes
...
; Definition des formes
...
; Principal
...
Attention, l'ordre est important, on ne peut appeler une fonction si elle n'a pas été déclarée en amont.
Dessinons un joli logo
Il semble que toutes les règles soient maintenant définies, plus qu'à écrire le programme et essayer de faire une oeuvre d'art avec la tortue.
La source complète en LOGO est sur mon github
Le programme complet fonctionne bien.
La structuration et la variabilisation permettent même d'en faire varier la sortie à volonté. J'ai pris quelques raccourcis mathématiques pour les tracés et travaillé avec des valeurs entières, ce qui fait que toutes les échelles des dessins ne sont pas parfaites.
Conclusion
Pour moi, la plus grosse difficulté a finalement été d'écrire tout en français sans y mélanger d'anglais. Mais l'exercice a été très distrayant à réaliser (et j'espère un peu distrayant à lire pour vous).
Je dirais pour conclure qu'on peut toujours facilement écrire du code illisible avec n'importe quel langage mais qu'on peut pourtant obtenir du code maintenable dans la plupart des cas. L'important venant des contraintes d'écriture que l'on doit s'imposer dès le départ.
Si ce projet avait été un travail d'équipe concret, la mise en place d'un vrai linter serait devenue une réelle nécessité pour garantir l'intégrité du code.
Pour résumer, le "code parfait" est tout relatif. Certains principes sont génériques, mais pour beaucoup ils dépendent essentiellement du contexte. Je vous conseillerai donc de vous efforcer de ne pas pratiquer l'écriture de code religieusement, avec des principes universels, mais pragmatiquement, en vous adaptant à votre environnement et à vos besoins.
Je me demande maintenant si je pourrai arriver à la même conclusion en utilisant du COBOL ... ;)