Ce langage est incomplet dans le sens où vous verrez souvent {js ...}
qui permet d'inclure du code JavaScript.
Alors quel avantage a-t-il ? Mon but était de permettre une structure de code plus naturelle et plus lisible (Je sais, c'est subjectif).
Pour le voir, je propose de commenter très brièvement ici le code (simplifié) de l'aquarium.
La partie principale du code
{par
*supplLifeOf
(lifeOfFish 'Mérou' 0)
(lifeOfFish 'Mérou' 1)
(lifeOfFish 'Thon' 0)
(lifeOfFish 'Thon' 1)
(lifeOfFish 'Poisson-clown' 0)
(lifeOfFish 'Poisson-clown' 1)
(lifeOfFish 'Sole' 0)
(lifeOfFish 'Sole' 1)
(lifeOfFish 'Bar' 0)
(lifeOfFish 'Bar' 1)
(lifeOfFish 'Carpe' 0)
(lifeOfFish 'Carpe' 1)
}
par
est une instruction FuncSug permettant de lancer des blocs de code en parallèle.
*supplLifeOf
permettra de rajouter dynamiquement des branches parallèles supplémentaires.
lifeOfFish
est le nom d'une fonction (qui est définie avant dans le code, j'en parle juste après).
(lifeOfFish 'Thon' 0)
appelle la fonction lifeOfFish
avec les arguments 'Thon'
et 0
.
Ainsi, tous les appels de fonction lifeOfFish
sont lancés et s'exécutent en parallèle.
Nous allons voir que lifeOfFish
est une fonction qui fait naître le poisson et continue de s'exécuter jusqu'à sa mort.
La fonction lifeOfFish
Voilà le code de la fonction :
{deffunc lifeOfFish (p_race p_sexe)
# naissance
#==========
# dessin du poisson, etc.
.var thisFish <-- (drawNewFish 100 30 0)
...
# après la naissance
#===================
{par @life
# gestion des collisions
#-----------------------
{whileTrueAwaitFrame `
// ---> Début de code JavaScript <---
// collisions
//-----------
for (const otherFish of ...) {
// Quand des poissons se rencontrent, ça génère une nouvelle naissance
if ( colliObj(thisFish, otherFish) ) {
sugBip('newBirth')
}
}
// fin de vie si ...
//------------------
if (...) sugBreak('life')
// ---> Fin de code JavaScript <---
`}
# gestion des naissances
#--------------------------------
{whileTrue
# on attend un signal 'newBirth'
:await newBirth beep
# on ajoute une nouvelle branche parallèle branch dans le bloc 'par' qui a '*supplLifeOf'
{spawn supplLifeOf
(lifeOfFish $p_race 0)
}
}
# mouvements du poisson
#----------------------
{whileTrueAwaitFrame `
// ---> Début de code JavaScript <---
thisFish.setAttribute('x', thisFish.x + thisFish.dx)
thisFish.setAttribute('y', thisFish.y + thisFish.dy)
// ---> Fin de code JavaScript <---
`}
}
# mort
#=====
# suppression du poisson de l'aquarium
}
{deffunc lifeOfFish (p_race p_sexe) ...}
permet de définir une fonction à deux arguments p_race
et p_sexe
.
.var maVariable <-- maValeur
permet de déclarer une variable et de l'initialiser.
Après la naissance, le poisson gère plusieurs choses à la fois (c.-à-d. en parallèle) :
- Les collisions avec les autres poissons
- La naissance de ses enfants
- Ses propres mouvements
Il y a donc une instruction par
avec les trois blocs correspondants. @life
est juste une étiquette pour pouvoir référencer le bloc par
(notamment pour l'instruction ```sugBreak('life') qui interrompt le bloc).
L'instruction whileTrueAwaitFrame
permet d'exécuter du code JavaScript à chaque rafraichissement d'écran. Dans ce code JavaScript, on peut utiliser sugBip
pour lancer un signal FuncSug ou sugBreak
pour interrompre un bloc FuncSug.
L'instruction {spawn monEtiquetteEtoile monNouveauBloc}
permet d'ajouter dynamiquement monNouveauBloc
dans le bloc par
dont le premier élément est *monEtiquetteEtoile
.
# L’aquarium
Posté par SpaceFox (site web personnel, Mastodon) . Évalué à 9.
Si on m’avait dit à l’époque que l’exercice que j’avais créé pour montrer à un stagiaire qu’il y a des problèmes qui se modélisent « naturellement » avec des objets en Java mais que dans la réalité c’est pas si simple, serait encore utilisé presque 13 ans plus tard… ben je ne l’aurais pas cru :D
Le plus drôle, c’est que je n’ai presque jamais vu d’implémentation du point 3.3 (là où ça ne devient plus « naturel » dans beaucoup de langages) et, à ma connaissance, aucun des points 4+.
La connaissance libre : https://zestedesavoir.com
[^] # Re: L’aquarium
Posté par cli345 . Évalué à 4.
Merci SpaceFox. :-)
J'avais fait cet exercice parce qu'il me semble que l'implémentation dans FuncSug est nettement plus simple que dans les langages habituels.
Je pense que cela est dû à ce que ce langage permet d'exprimer facilement le parallélisme logique.
Je vais donc essayer de faire les points 3.3 et 4. ;-)
[^] # Re: L’aquarium
Posté par jtremesay (site web personnel) . Évalué à 4. Dernière modification le 20 mars 2023 à 19:00.
J'avoue.
J'ai écrit
Et j'ai bien eu mon alternance de
1
et2
dans la console sans avoir à me prendre la tête avec des histoire de threads ou de await.La détection et la terminaison des boucles infinis, c'est une feature ou une limite de l'interpreteur ?
[^] # Re: L’aquarium
Posté par cli345 . Évalué à 2.
J'ai oublié de retirer le mode debug dans l'interpréteur. J'avais mis une limite arbitraire pour faciliter mon développement.
[^] # Re: L’aquarium
Posté par jtremesay (site web personnel) . Évalué à 4.
ok :)
[^] # Re: L’aquarium
Posté par cli345 . Évalué à 0.
J'ai enlevé le mode debug et ajouté une instruction
setDebug
.[^] # Re: L’aquarium
Posté par cli345 . Évalué à 0.
J'ai continué l'exercice avec le point 3.3.
[^] # Re: L’aquarium
Posté par cli345 . Évalué à 0.
Tel que je l'ai fait, les poissons hermaphrodites opportunistes ne changent de sexe que lorsque les deux poissons ont l'âge de procréer.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.