Le sujet TapTempo est très intéressant pour apprendre un nouveau langage de programmation. En effet, il est beaucoup plus complet qu'un simple Hello World ! Il nous impose de se plonger dans les arcanes du langage et de ses outils pour gérer le temps, l'affichage mais aussi les arguments de la ligne de commande ou les structures «complexe» comme les listes ou les buffers.
Bon évidemment, quand il s'agit d'un langage de description hardware comme présenté la dernière fois avec Chisel on évite la ligne de commande ! Par contre, Chisel étant basé sur le langage Scala, il est intéressant de se plonger dedans histoire de le maitriser un minimum.
Voici donc mon humble version de TapTempo en Scala. Langage étrange au paradigme bipolaire (;)) : Fonctionnel et Objet. Ce langage a déjà été décrit dans les colonnes de LinuxFR, il est basé sur une machine virtuelle Java et permet d'appeler les classes et fonctions de ce même langage. Et j'ai appris récemment que la réciproque est également vraie : On peut appeler les classes/fonctions Scala en Java.
// TapTempo
import jline.console.ConsoleReader /* to read keyboard */
import scala.collection._
import sys.process._ /* shell cmd execution with ! */
val VERSION = "1.0"
val PRECISION = 0
val PRECISIONLIST = List("%.00f", "%.01f", "%.02f", "%.03f", "%.04f", "%.05f")
val RESET_TIME = 5
val SAMPLE_SIZE = 5
val SECOND = 1e9
val MIN = 60*SECOND
object TapTempo {
def usages() {
println("-h, --help affiche ce message d'aide")
println("-p, --precision changer le nombre de décimale du tempo à afficher")
println(" la valeur par défaut est 0 décimales, le max est 5 décimales")
println("-r, --reset-time changer le temps en seconde de remise à zéro du calcul")
println(" la valeur par défaut est 5 secondes")
println("-s, --sample-size changer le nombre d'échantillons nécessaires au calcul du tempo")
println(" la valeur par défaut est 5 échantillons")
println("-v, --version afficher la version")
}
def printversion() {
println("TapTempo Scala version " + VERSION)
}
def tempo(tfifo: mutable.Buffer[Double]):Double = {
var sum = 0.0
tfifo.foreach(sum += _)
sum/tfifo.length
}
def main(args: Array[String]) {
val arglist = args.toList
type OptionMap = Map[Symbol, Int]
/****************/
/* parsing args */
/****************/
def nextOption(map : OptionMap, list: List[String]) : OptionMap = {
def isSwitch(s : String) = (s(0) == '-')
list match {
case Nil => map
case ("-h" | "--help") :: tail => usages(); sys.exit(0)
case ("-v" | "--version") :: tail => printversion; sys.exit(0)
case ("-p" | "--precision") :: value :: tail =>
nextOption(map ++ Map('precision -> value.toInt), tail)
case ("-r" | "--reset-time") :: value :: tail =>
nextOption(map ++ Map('rtime -> value.toInt), tail)
case ("-s" | "--sample-size") :: value :: tail =>
nextOption(map ++ Map('ssize -> value.toInt), tail)
case option :: tail => println("Unknown option " + option)
sys.exit(0)
}
}
val options = nextOption(Map(), arglist)
var precision = options.getOrElse('precision, -1)
if(precision < 0 || precision > 5)
precision = PRECISION
var rtime = options.getOrElse('rtime, -1)
if(rtime == -1)
rtime = RESET_TIME
var ssize = options.getOrElse('ssize, -1)
if(ssize == -1)
ssize = SAMPLE_SIZE
/* Minimum caracters for completed read */
(Seq("sh", "-c", "stty -icanon min 1 < /dev/tty") !)
/* Do not print input caracters */
(Seq("sh", "-c", "stty -echo < /dev/tty") !)
var timefifo = mutable.Buffer.fill[Double](ssize)(0.0)
var fifocount = 0
println("Appuyer sur une touche en cadence (q pour quitter).")
var c = 0
var i = 0
var current_time = System.nanoTime()
var old_time = System.nanoTime()
do {
c = Console.in.read
current_time = System.nanoTime()
val tempotime = MIN/(current_time - old_time)
if(tempotime < 60/rtime){
fifocount = 0
}
if(fifocount < ssize){
fifocount += 1
}
timefifo(i) = tempotime
i = (i + 1) % ssize
if(fifocount == ssize){
printf("Tempo : " + PRECISIONLIST(precision) + "\n", tempo(timefifo))
} else {
printf("Tempo : %d/%d\n", fifocount, ssize)
}
old_time = current_time
} while (c != 113) // While 'q' is pressed
println("Bye Bye!")
}
}
TapTempo.main(args)
Voila voila, le programme est également disponible sur mon github (oui oui je sais çémal). Ce petit exercice m'a permis de découvrir un peu plus de ce langage suisse très prisé du monde de la finance.
Mais il y a encore du chemin pour le maîtriser.
À oui j’oubliai, pour le lancer en tant que script faire :
$ scala -nc TapTempo.sc
Patienter un certain temps — hé oui on parle quand même d'une machine virtuel java et de compilation de bytecode, çélong ! — puis taper la touche de votre choix, 'q' pour quitter.
Appuyer sur une touche en cadence (q pour quitter).
Tempo : 1/5
Tempo : 2/5
Tempo : 3/5
Tempo : 4/5
Tempo : 152
Tempo : 186
Tempo : 195
Tempo : 164
Tempo : 159
Tempo : 162
Tempo : 157
Tempo : 156
Tempo : 159
Bye Bye!
# Objet et Fonctionnel
Posté par Victor . Évalué à 6.
C'est marrant, parce que tu débutes le journal en disant que Scala est un langage Objet et Fonctionnel, mais le programme que tu nous partages est ni objet, ni fonctionnel.
C'est du code procédural assez classique.
Ce n'est pas forcément un mal, mais je suis curieux de savoir si la question s'était posée et si il n'y aurait pas d'autres designs plus objet et/ou fonctionnel :)
[^] # Re: Objet et Fonctionnel
Posté par Yves (site web personnel) . Évalué à 2.
La réponse est oui, Victor, tu as raison :-) Pas le temps d’élaborer, peut-être l’auteur du journal le fera-t-il…
[^] # Re: Objet et Fonctionnel
Posté par martoni (site web personnel, Mastodon) . Évalué à 3. Dernière modification le 24 juillet 2018 à 10:03.
La réponse est oui certainement ;)
Il serait intéressant de transformer le programme pour qu'il soit plus fonctionnel/objet en effet.
J'ai plus qu'une balle
[^] # Re: Objet et Fonctionnel
Posté par j_m . Évalué à 4. Dernière modification le 24 juillet 2018 à 20:25.
Un truc facile pour commencer c'est de remplacer tous les
var
par desval
. Il y a peut-etre plus simple pour le precision…[^] # Re: Objet et Fonctionnel
Posté par j_m . Évalué à 3.
Voila un precision plus simple:
# Faut se calmer, là !
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 3. Dernière modification le 24 juillet 2018 à 14:43.
Une fonction de 76 lignes, ça va pas là !
Allez, hop, tu nous refais ça avec des fonctions de 5 lignes maximum.
[^] # Re: Faut se calmer, là !
Posté par martoni (site web personnel, Mastodon) . Évalué à 2.
Aucune chances, rien que l'affichage du help fait plus de 5 lignes. ;)
J'ai plus qu'une balle
[^] # Re: Faut se calmer, là !
Posté par j_m . Évalué à 3. Dernière modification le 24 juillet 2018 à 20:16.
Ca compte les donnees? Sinon l'affichage du help se reduit a une ligne:
[^] # Re: Faut se calmer, là !
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 4.
C'est mieux, oui !
[^] # Re: Faut se calmer, là !
Posté par Tit . Évalué à 3.
pourquoi c'est mieux ?
[^] # Re: Faut se calmer, là !
Posté par martoni (site web personnel, Mastodon) . Évalué à 2.
J'avoue que je me pose la même question ;)
Ma fonction faisait 10 lignes, celle-ci en fait 12 !
Après peut-être y a-t-il une «loi» de séparation du code fonctionnel et des données que je ne respecte pas en effet.
J'ai plus qu'une balle
[^] # Re: Faut se calmer, là !
Posté par Marco . Évalué à 3. Dernière modification le 25 juillet 2018 à 09:45.
Tu t'en rends pas compte car c'est un petit programme.
Mais admettons que tu dois afficher le messages d'aides à deux endroits différents dans le code :
Dans un cas tu dois copier coller tout le texte, dans l'autre tu dois juste copier une ligne
Admettons que tu t’aperçoives qu'il y a une faute d'orthographe :
[^] # Re: Faut se calmer, là !
Posté par Marco . Évalué à 2.
J'ai dit des bêtises, mon argument ne tient pas puisque c'est une fonction.
Enfin bref c'est mieux de séparer code et message car ce ne sont pas forcément les mêmes personnes qui bossent dessus (par exemple pour une traduction on est obligé d'aller dans le code)
[^] # Re: Faut se calmer, là !
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 2.
J'appellerais pas ça une "loi", mais oui, l'idée de séparer les données du code est plutôt bonne. C'est peut-être un effet secondaire dû à des années de Spring/Guice couplée à un design générique pour gérer le rendu des options (pour générer des manfiles, de la doc audio, etc.).
# Régionalisation
Posté par mzf (site web personnel) . Évalué à 2. Dernière modification le 25 juillet 2018 à 14:00.
Chouette un nouveau portage, bravo :-) Il ne manque plus que la régionalisation pour qu'il soit conforme à l'original. Est-ce facile en Scala ?
[^] # Re: Régionalisation
Posté par martoni (site web personnel, Mastodon) . Évalué à 2.
Je pense que c'est possible en effet. Déjà en intégrant les propositions de j_m on devrait pouvoir isoler les données (texte) du code.
J'ai plus qu'une balle
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.