L’objectif de ce document est de vous donner un aperçu rapide du langage de programmation Raku.
Pour ceux qui sont nouveaux à Raku, il devrait vous permettre de démarrer.
Certaines sections de ce document référencent d’autres parties (plus complètes et précises) de la documentation de Raku. Vous devriez les lire si vous avez besoin de plus d’information sur un sujet précis.
En lisant ce document, vous trouverez des exemples pour la plus part des sujets discutés. Pour mieux les comprendre, vous pouvez expérimenter en les modifiant.
Ce document est sous licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International. Pour accéder à une copie de cette licence, merci de vous rendre à l’adresse suivante
Si vous souhaitez contribuer à ce document rendez-vous à l’adresse suivante:
1. Introduction
1.1. Raku c’est quoi?
Raku est un langage de haut niveau, générique et dynamique. Il supporte plusieurs paradigmes dont : la programmation procédurale, la programmation orientée objet et la programmation fonctionnelle.
-
TMTOWTDI (prononcé Tim Toady): There is more than one way to do it: c’est-à-dire « Il y a plus d’une façon de le faire »
-
Easy things should stay easy, hard things should get easier, and impossible things should get hard: « Les choses faciles doivent rester faciles, les choses difficiles devraient devenir plus faciles, et les choses impossibles devraient devenir difficiles »
Un programme ou script Raku est un fichier texte qui sera compilé et exécuté par l’exécutable raku
ou perl6
et la machine virtuelle associée (par exemple MoarVM ou JVM).
1.2. Jargon
-
Raku est une spécification de langage avec une suite de tests. Les implémentations qui passent la suite de tests sont considérées comme du Raku.
-
Rakudo est un compilateur pour Raku.
-
Rakudobrew est un gestionnaire d’installation pour Rakudo.
-
Zef est un installeur de modules pour Raku.
-
Rakudo Star est un paquet qui comprend : Rakudo, Zef, une collection de modules, et de la documentation.
1.3. Installer Raku
mkdir ~/rakudo && cd $_
curl -LJO https://rakudo.org/latest/star/src
tar -xzf rakudo-star-*.tar.gz
mv rakudo-star-*/* .
rm -fr rakudo-star-*
./bin/rstar install
echo "export PATH=$(pwd)/bin/:$(pwd)/share/perl6/site/bin:$(pwd)/share/perl6/vendor/bin:$(pwd)/share/perl6/core/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc
Quatre solutions possibles :
-
Suivez les mêmes étapes que celles indiquées pour l’installation sur Linux
-
Installer avec homebrew:
brew install rakudo-star
-
Installez avec MacPorts:
sudo port install rakudo
-
Téléchargez l’installateur (fichier avec l’extension .dmg) depuis https://rakudo.org/latest/star/macos
-
Pour les architectures 64-bit : Téléchargez le dernier installeur (extension .msi) ici https://rakudo.org/latest/star/win
-
Après l’installation, assurez vous que
C:\rakudo\bin
soit dans votre PATH.
-
Obtenez l’image officielle
docker pull rakudo-star
-
Ensuite exécutez
docker run -it rakudo-star
1.4. Exécuter du code Raku
On peut exécuter du code Raku en utilisant le terminal Raku interactif REPL (Read-Eval-Print Loop).
Pour ce faire, ouvrez un terminal, tapez raku
ou perl6
dans la fenêtre de terminal et ensuite la touche [Entrée].
Une invite de commande >
apparaîtra.
Ensuite, tapez une ligne de code puis la touche [Entrée]. Le REPL affichera la valeur de la ligne interprétée.
Vous pouvez taper une autre ligne, ou exit
et ensuite [Entrée] pour sortir du REPL.
L’autre façon consiste à écrire votre code dans un fichier, le sauvegarder puis l’exécuter.
Il est conseillé, pour plus de clarté, que les scripts Raku portent l’extension .raku
.
Exécutez le fichier en entrant raku
ou perl6
nom-du-fichier.raku
dans la fenêtre de terminal (puis [Entrée]).
À l’inverse du REPL, le résultat ne sera pas automatiquement affiché pour chaque ligne:
le code doit contenir une instruction comme say
ou print
pour afficher une sortie.
Le REPL est la plupart du temps utilisé pour essayer un morceau de code, le plus souvent une seule ligne. Pour des programmes de plus d’une seule ligne, la méthode fichier/exécution est recommandée.
Les lignes de code unilignes peuvent aussi être entrées de façon non interactive sur la ligne de commande en tapant raku -e 'mon code ici'
ou perl6 -e 'mon code ici'
et ensuite [Entrée].
Rakudo Star fournit un éditeur ligne par ligne, qui augmente les fonctionnalités du REPL. Comme: le rappel des commandes par les flèches « haut/bas », l’édition avec les flèches « gauche/droite » et la complétion avec la touche [TAB]. Si vous avez seulement installé Rakudo au lieu de Rakudo Star, vous n’aurez probablement pas les fonctions d’édition ligne par ligne. Lancez la commande suivante sur votre terminal pour y avoir accès:
|
1.5. Editeurs
Comme la plupart du temps, nous allons écrire et stocker nos programmes en Raku dans des fichiers, nous devrions avoir un éditeur de texte décent qui reconnaît la syntaxe de Raku.
Les versions récentes de Vim sont livrées avec la coloration syntaxique pour Raku. Emacs et Padre nécessiteront l’installation de paquets supplémentaires.
N’importe quel éditeur de texte peut être utilisé pour écrire ou lire un programme Raku. |
1.6. Bonjour Monde!
Nous allons commencer avec le rituel hello world
.
say 'Bonjour Monde';
qui peut aussi s’écrire:
'Bonjour Monde'.say;
1.7. Aperçu de la syntaxe
Raku a une forme libre: vous êtes libre (la plupart du temps) d’utiliser n’importe quelle quantité d’espaces.
Les instructions sont typiquement une ligne logique de code, elles doivent se terminer par un point-virgule:
say "Hello" if True;
Les expressions sont un type spécial d’instructions qui retournent une valeur:
1+2
retourne 3
Les expressions sont faites de termes et d'opérateurs.
Les termes sont des:
-
variables: une valeur qui peut être manipulée ou changée.
-
valeurs littérales: une valeur constante comme un nombre ou une chaîne.
Les opérateurs sont classés en types:
Type |
Explication |
Exemple |
Préfixé |
Avant le terme. |
|
Infixé |
Entre deux termes. |
|
Suffixé |
Après le terme. |
|
Circonfixé |
Autour du terme. |
|
Postcirconfixé |
Après un terme, autour d’un autre |
|
1.7.1. Identificateurs
Les identificateurs sont le nom donné aux termes lors de leur définition.
-
ils doivent commencer par un caractère alphabétique ou un tiret bas (underscore).
-
ils peuvent contenir des chiffres (à l’exception du premier caractère).
-
ils peuvent contenir des tirets ou des apostrophes (sauf le premier et le dernier caractère), mais avec un caractère alphabétique à droite de chaque tiret apostrophe.
Valide |
Non valide |
|
|
|
|
|
|
|
|
|
|
-
Camel:
variableNo1
-
Kebab:
variable-no1
-
Snake:
variable_no1
Vous êtes libre de nommer vos identificateurs comme vous le souhaitez, mais, pour des raisons de cohérence et de lisibilité, il est recommandé de choisir une convention de nommage et de s’y tenir.
L’utilisation de noms signifiants facilitera votre vie et celle des autres.
var1 = var2 * var3
est syntaxiquement correct, mais son but n’est pas évident.
salaire-mensuel = salaire-journalier * jours-travaillés
serait une meilleure façon de nommer vos variables.
1.7.2. Commentaires
Un commentaire est du texte ignoré par le compilateur.
Il y a 3 types de commentaires:
-
ligne unique:
#Ceci est une seule ligne de commentaire
-
intégré:
say #`(Ceci est un commentaire intégré) "Bonjour Monde."
-
multi ligne:
=begin comment Ceci est un commentaire sur plusieurs lignes. Commentaire 1 Commentaire 2 =end comment
1.7.3. Guillemets
Les chaînes doivent être délimitées par des guillemets droits, doubles ou simples (apostrophes).
Utilisez toujours des guillemets droits doubles:
-
si votre chaîne contient une apostrophe.
-
si votre chaîne contient une variable qui doit être interpolée.
say 'Bonjour Monde'; #Bonjour Monde
say "Bonjour Monde"; #Bonjour Monde
say "Quelqu'un m'a dit"; #Quelqu'un m'a dit
my $nom = 'François Pinon';
say 'Salut $nom'; #Salut $nom
say "Salut $nom"; #Salut François Pinon
2. Opérateurs
Operateur | Type | Description | Exemple | Résultat |
---|---|---|---|---|
|
|
Addition |
|
|
|
|
Soustraction |
|
|
|
|
Multiplication |
|
|
|
|
Puissance |
|
|
|
|
Division |
|
|
|
|
Division (arrondit vers le bas) |
|
|
|
|
Modulo (reste de la division entière) |
|
|
|
|
Divisibilité |
|
|
|
|
|||
|
|
Plus grand dénominateur commun |
|
|
|
|
Plus petit commun multiple |
|
|
|
|
Egalité |
|
|
|
|
Inégalité |
|
|
|
|
Plus petit |
|
|
|
|
Plus grand |
|
|
|
|
Plus petit ou égal |
|
|
|
|
Plus grand ou égal |
|
|
|
|
Egalité (chaînes) |
|
|
|
|
Inégalité (chaînes) |
|
|
|
|
Affectation |
|
|
|
|
Concaténation |
|
|
|
|
|||
|
|
Réplication |
|
|
|
|
|||
|
|
Smart match (reconnaissance intelligente) |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Incrémentation |
|
|
|
Incrémentation |
|
|
|
|
|
Décrémentation |
|
|
|
Décrémentation |
|
|
|
|
|
Force l’opérande a une valeur numérique |
|
|
|
|
|||
|
|
|||
|
|
Force l’opérande a une valeur numérique et retourne la négation |
|
|
|
|
|||
|
|
|||
|
|
Force l’opérande a une valeur booléenne |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Force l’opérande a une valeur booléenne et retourne la négation |
|
|
|
|
Construction d’intervalles |
|
|
|
|
Construction d’intervalles |
|
|
|
|
Construction d’intervalles |
|
|
|
|
Construction d’intervalles |
|
|
|
|
Construction d’intervalles |
|
|
|
|
Construction de listes paresseuses |
|
|
|
|
Aplanissement |
|
|
|
|
Pour la liste complète des opérateurs, y compris leur priorité: https://docs.raku.org/language/operators |
3. Variables
Les variables sont classées en trois catégories : scalaires, tableaux et hachages.
Un sigil (signe en Latin) est un caractère utilisé comme préfixe pour classer les variables.
-
$
est utilisé pour les scalaires -
@
est utilisé pour les tableaux -
%
est utilisé pour les tables de hachage.
3.1. Scalaire
Un scalaire contient une valeur ou une référence.
#String
my $nom = 'François Pinon';
say $nom;
#Integer
my $age = 20;
say $age;
Certaines opérations peuvent être effectuées sur un scalaire, suivant le type de valeur qu’il contient.
my $nom = 'François Pinon';
say $nom.uc;
say $nom.chars;
say $nom.flip;
FRANÇOIS PINON
14
noniP sioçnarF
Pour une liste exhaustive des méthodes applicables aux chaînes, voir https://docs.raku.org/type/Str |
my $age = 17;
say $age.is-prime;
True
Pour une liste exhaustive des méthodes applicables aux entiers, voir https://docs.raku.org/type/Int |
my $age = 2.3;
say $age.numerator;
say $age.denominator;
say $age.nude;
23
10
(23 10)
Pour une liste exhaustive des méthodes applicables aux nombres rationnels, voir https://docs.raku.org/type/Rat |
3.2. Tableaux
Les tableaux sont des listes contenant plusieurs valeurs.
Par défaut, les valeurs d’un tableau peuvent être de différents types.
my @animaux = 'chameau','lama','hibou';
say @animaux;
De nombreuses opérations peuvent être effectuées sur les tableaux comme le montre l’exemple suivant:
Le tilde ~ est utilisé pour la concaténation.
|
Script
my @animaux = 'chameau','vigogne','lama';
say "Le zoo contient " ~ @animaux.elems ~ " animaux";
say "Les animaux sont: " ~ @animaux;
say "Je vais adopter un hibou pour le zoo";
@animaux.push("hibou");
say "Maintenant, mon zoo contient: " ~ @animaux;
say "Le premier animal que nous avons adopté est le " ~ @animaux[0];
@animaux.pop;
say "Malheureusement, le hibou est parti, il ne nous reste que: " ~ @animaux;
say "Nous allons fermer le zoo et laisser un animal seulement";
say "Nous allons faire partir: " ~ @animaux.splice(1,2) ~ " et laisser le " ~ @animaux;
Sortie
Le zoo contient 3 animaux
Les animaux sont: chameau vigogne lama
Je vais adopter un hibou pour le zoo
Maintenant, mon zoo contient: chameau vigogne lama hibou
Le premier animal que nous avons adopté est le chameau
Malheureusement, le hibou est parti, il ne nous reste que: chameau vigogne lama
Nous allons fermer le zoo et laisser un animal seulement
Nous allons faire partir: vigogne lama et laisser le chameau
.elems
retourne le nombre d’éléments contenus dans le tableau.
.push()
ajoute un élément au tableau.
Nous pouvons accéder à un élément spécifique dans le tableau en spécifiant sa position @animaux[0]
.
.pop
supprime le dernier élément du tableau.
.splice(a,b)
supprime les b
éléments à partir de la position a
.
3.2.1. Tableaux de taille fixe
Un tableau simple se déclare comme ceci:
my @tableau;
Le tableau simple à une taille non définie, et peut varier de façon automatique.
Ce tableau acceptera un nombre illimité de valeurs sans restriction.
On peut en revanche créer des tableaux de taille fixe.
Ces tableaux ne pourront pas excéder la taille qui leur aura été allouée (en lecture et écriture).
Pour déclarer un tableau de taille fixe, spécifiez son nombre maximal d’éléments entre crochets à la suite de son nom:
my @tableau[3];
Ce tableau pourra contenir un maximum de trois valeurs, indexées de 0 à 2.
my @tableau[3];
@tableau[0] = "première valeur";
@tableau[1] = "deuxième valeur";
@tableau[2] = "troisième valeur";
Vous ne pourrez pas ajouter une quatrième valeur à ce tableau:
my @tableau[3];
@tableau[0] = "première valeur";
@tableau[1] = "deuxième valeur";
@tableau[2] = "troisième valeur";
@tableau[3] = "quatrième valeur";
Index 3 for dimension 1 out of range (must be 0..2)
3.2.2. Tableaux à plusieurs dimmensions
Les tableaux vus précédemment ne sont qu’à une dimension.
Heureusement, nous pouvons en Raku déclarer des tableaux de dimensions multiples.
my @multi-tab[3;2];
Ce tableau a deux dimensions. La première dimension peut contenir un maximum de trois valeurs et la seconde un maximum de deux valeurs.
my @multi-tab[3;2];
@multi-tab[0;0] = 1;
@multi-tab[0;1] = "x";
@multi-tab[1;0] = 2;
@multi-tab[1;1] = "y";
@multi-tab[2;0] = 3;
@multi-tab[2;1] = "z";
say @multi-tab
[[1 x] [2 y] [3 z]]
Pour la référence complète des tableaux: https://docs.raku.org/type/Array |
3.3. Hachage
my %capitales = ('Angleterre','Londres','France','Paris');
say %capitales;
my %capitales = (Angleterre => 'Londres', France => 'Paris');
say %capitales;
Voici quelques-unes des méthodes qui peuvent être appelées sur les hachages:
Script
my %capitales = (Angleterre => 'Londres', Allemagne => 'Berlin');
%capitales.push: (France => 'Paris');
say %capitales;
say %capitales.kv;
say %capitales.keys;
say %capitales.values;
say "La capitale de la France est: " ~ %capitales<France>;
Sortie
{Allemagne => Berlin, Angleterre => Londres, France => Paris}
(France Paris Allemagne Berlin Angleterre Londres)
(France Allemagne Angleterre)
(Paris Berlin Londres)
La capitale de la France est: Paris
.push: (clef => 'valeur')
ajoute une nouvelle paire clef/valeur.
.kv
renvoie la liste contenant toutes les clefs et valeurs.
.keys
renvoie une liste des clefs.
.values
renvoie une liste des valeurs.
On peut accéder à la valeur particulière d’un hachage en spécifiant sa clef, comme suit: %hachage<clef>
Pour la référence complète des hachages: https://docs.raku.org/type/Hash |
3.4. Types
Dans les exemples précédents, on n’a pas précisé quel type de valeurs les variables peuvent contenir.
.WHAT retournera le type de la valeur contenue dans la variable.
|
my $var = 'Texte';
say $var;
say $var.WHAT;
$var = 123;
say $var;
say $var.WHAT;
Comme vous pouvez le voir dans l’exemple ci-dessus, le type de valeur contenu dans $var
était (Str) et puis (Int).
Ce style de programmation est appelé le typage dynamique. Dynamique dans le sens que les variables peuvent contenir des valeurs de tout type.
Maintenant, essayez d’exécuter l’exemple ci-dessous:
Remarquez Int
avant le nom de la variable.
my Int $var = 'Texte';
say $var;
say $var.WHAT;
Il va échouer et retourner ce message d’erreur: Type check failed in assignment to $var; expected Int but got Str
Ce qui est arrivé est que nous avons précisé au préalable que la variable doit être de type (Int
).
Quand nous avons essayé de lui affecter un (Str
), le programme a échoué.
Ce style de programmation est appelé le typage statique. Statique dans le sens que les types de variables sont définis avant l’affectation et ne peuvent pas changer.
Raku possède un typage graduel, les deux typages, statique et dynamique, peuvent être utilisés.
Les deux premiers ne seront probablement jamais utilisés, mais ils sont répertoriés à titre informatif.
|
|
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.5. Introspection
L’introspection est le processus d’obtention d’informations sur les propriétés d’un objet comme son type.
Dans l’exemple précédent, nous avons utilisé .WHAT
pour connaître le type de la variable.
my Int $var;
say $var.WHAT; # (Int)
my $var2;
say $var2.WHAT; # (Any)
$var2 = 1;
say $var2.WHAT; # (Int)
$var2 = "Hello";
say $var2.WHAT; # (Str)
$var2 = True;
say $var2.WHAT; # (Bool)
$var2 = Nil;
say $var2.WHAT; # (Any)
Le type d’une variable contenant une valeur est corrélé à sa valeur.
Le type d’une variable vide fortement déclarée est le type avec lequel elle a été déclarée.
Le type d’une variable vide qui n’a pas été déclarée fortement est (Any
)
Pour vider la valeur d’une variable, vous pouvez lui affecter Nil
.
3.6. Portée
Avant d’utiliser une variable pour la première fois, elle doit être déclarée.
Plusieurs déclarateurs peuvent être utilisés dans Raku, my
est ce que nous avons utilisé jusqu’ici.
my $var=1;
Le déclarateur my
donne à la variable une portée lexicale.
En d’autres termes, la variable ne sera accessible que dans le bloc où elle a été déclarée.
Un bloc en Raku est délimité par { }
.
Si aucun bloc n’est trouvé, la variable sera disponible dans l’ensemble du script (on dit alors parfois qu’elle est globale au script).
{
my Str $var = 'Texte';
say $var; #accessible
}
say $var; #inaccessible, renvoie une erreur
Comme une variable est uniquement accessible dans le bloc où elle est définie, le même nom de variable peut être redéfini dans un autre bloc.
{
my Str $var = 'Texte';
say $var;
}
my Int $var = 123;
say $var;
3.7. Affecter vs. Lier
Nous avons vu dans les exemples précédents comment affecter des valeurs aux variables.
L'affectation est faite en utilisant l’opérateur =
my Int $var = 123;
say $var;
Nous pouvons modifier la valeur attribuée à une variable:
my Int $var = 123;
say $var;
$var = 999;
say $var;
Sortie
123
999
D’autre part, nous ne pouvons pas changer la valeur liée à une variable.
Le lien est établi en utilisant l’opérateur :=
my Int $var := 123;
say $var;
$var = 999;
say $var;
Sortie
123
Cannot assign to an immutable value
my $a;
my $b := $a;
$a = 7;
say $b;
Un lien ne peut être créé que lors de l’initialisation de la variable liée, et ne peut plus être modifié ensuite. Mais la valeur de la variable liée peut néanmoins changer si la valeur de la variable « maîtresse » à laquelle elle est liée change.
Pour plus d’informations sur les variables, rendez-vous à https://docs.raku.org/language/variables |
4. Fonctions normales et fonctions mutatrices
Il est important de différencier entre les fonctions normales et les fonctions mutatrices.
Les fonctions normales ne changent pas l’état initial de l’objet.
Les fonctions mutatrices modifient l’état de l’objet.
Script
1
2
3
4
5
6
7
8
9
10
my @numeros = [7,2,4,9,11,3];
@numeros.push(99);
say @numeros; #1
say @numeros.sort; #2
say @numeros; #3
@numeros.=sort;
say @numeros; #4
Sortie
[7 2 4 9 11 3 99] #1
(2 3 4 7 9 11 99) #2
[7 2 4 9 11 3 99] #3
[2 3 4 7 9 11 99] #4
.push
est une fonction mutatrice, elle change l’état du tableau (#1)
.sort
est une fonction normale, elle retourne un tableau trié, mais ne modifie pas l’état initial du tableau:
-
(#2) démontre le retour d’un tableau trié
-
(#3) démontre que le tableau initial reste non modifié.
Afin de forcer une fonction normale à agir comme une fonction mutatrice, nous pouvons utiliser .=
a la place de .
(#4) (Ligne 9 du script)
5. Structures conditionnelles et boucles
Raku possède une multitude de structures conditionnelles et structures de boucles.
5.1. if
Le code ne s’exécute que si la condition a été remplie.
my $âge = 19;
if $âge > 18 {
say 'Bienvenue'
}
En Raku, nous pouvons inverser le code et la condition.
Même si le code et la condition ont été inversés, la condition est toujours évaluée en premier.
my $âge = 19;
say 'Bienvenue' if $âge > 18;
Si la condition n’est pas remplie, nous pouvons toujours préciser des blocs d’exécution alternatifs en utilisant:
-
else
-
elsif
#exécuter le même code pour différentes valeurs de la variable
my $nombre-de-places = 9;
if $nombre-de-places <= 5 {
say 'Je suis une berline'
} elsif $nombre-de-places <= 7 {
say 'Je suis un monospace'
} else {
say 'Je suis un van'
}
5.2. unless
La version négative d’un if
peut être écrite en utilisant unless
.
Le code suivant:
my $chaussures-propres = False;
if not $chaussures-propres {
say 'Nettoyez vos chaussures'
}
peut aussi être écrit ainsi:
my $chaussures-propres = False;
unless $chaussures-propres {
say 'Nettoyez vos chaussures'
}
La négation en Raku est faite en utilisant !
ou not
.
unless (condition)
est utilisé à la place de if not (condition)
.
unless
ne peux pas avoir une clause else
.
5.3. with
with
fonctionne comme if
, mais vérifie si la variable est définie.
my Int $var=1;
with $var {
say 'Bonjour'
}
Si vous exécutez le code sans attribuer une valeur à la variable, rien ne devrait arriver.
my Int $var;
with $var {
say 'Bonjour'
}
without
est la version négative de with
. Vous devriez être capable de le relier le concept à unless
.
Si la première condition with
n’est pas remplie, un autre chemin peut être spécifié en utilisant orwith
.
with
et orwith
peuvent être comparés à if
et elsif
.
5.4. for
La boucle for
itère sur plusieurs valeurs.
my @tableau = 1,2,3;
for @tableau -> $element {
say $element*100
}
Notez que nous avons créé une variable d’itération $element
afin d’effectuer l’opération *100
sur chaque élément du tableau.
Dans ce genre de construction, la variable d’itération $element
est autodéclarée et ne doit donc pas être précédée par le déclarateur my
.
5.5. given
given
est l’équivalent Raku de l’instruction switch
dans d’autres langages.
my $var = 42;
given $var {
when 0..50 { say 'Plus petit que 50'}
when Int { say "est un Int" }
when 42 { say 42 }
default { say "huh?" }
}
Si l’une des conditions est satisfaite, le processus d’appariement s’arrête (les autres conditions ne seront pas testées).
Le code ci-dessus n’affichera donc que Plus petit que 50
.
Si l’on préfère tester aussi les conditions suivantes, proceed
instruira Raku à poursuivre l’appariement, même après un appariement réussi.
my $var = 42;
given $var {
when 0..50 { say 'Plus petit que 50';proceed}
when Int { say "est un Int";proceed}
when 42 { say 42 }
default { say "huh?" }
}
5.6. loop
loop
est une autre façon d’écrire une boucle for
.
En fait loop
s’écrit comme le sont les boucles for
dans les langages de programmation appartenant a la famille C.
Raku appartient à la famille C.
loop (my $i=0; $i < 5; $i++) {
say "Le nombre actuel est $i"
}
Pour plus d’informations sur les boucles et les conditions, voir https://docs.raku.org/language/control |
6. Entrées/Sorties
En Raku, deux des interfaces entrée/sortie les plus communes sont le Terminal et les Fichiers.
6.1. E/S Basic en utilisant le Terminal
6.1.1. say
say
écrit sur la sortie standard (en général, l’écran). Il ajoute un caractère de fin ligne à la fin. Autrement dit, le code suivant:
say 'Bonjour Madame.';
say 'Bonjour Monsieur.';
sera écrit sur 2 lignes distinctes.
6.1.2. print
print
fonctionne comme say
, mais sans ajouter de caractère de fin ligne.
Essayez de remplacer say
avec print
et de comparer les deux résultats.
6.1.3. get
get
est utilisé pour capturer l’entrée du Terminal.
my $nom;
say "Salut quel est ton nom?";
$nom=get;
say "Cher $nom bienvenue à Raku";
Lorsque le code ci-dessus est lancé, le terminal attendra que vous saisissiez votre nom. Par la suite, il vous accueillera.
6.1.4. prompt
prompt
est une combinaison de print
et get
.
L’exemple ci-dessus peut être écrit comme ceci:
my $nom = prompt("Salut quel est ton nom? ");
say "Cher $nom bienvenue à Raku";
6.2. Exécution de commandes Shell
Deux routines peuvent être utilisées pour exécuter des commandes shell:
-
run
exécute une commande externe sans impliquer le shell -
shell
exécute une commande via le shell. Tous les métacaractères sont interprétés par le shell, y compris les tubes (pipes), les redirections, les variables d’environnement, etc.
my $nom = 'Neo';
my $commande = run 'echo', "salut $nom";
my $commande2 = shell "ls";
shell "dir";
echo
et ls
sont des mots-clefs communs des shells Unix ou Linux.
echo
imprime le texte sur le terminal (l’équivalent de say
en Raku)
ls
liste tous les fichiers et dossiers dans le répertoire courant sous Linux et dir
fait la même chose sous Windows.
6.3. E/S Fichier
6.3.1. slurp
slurp
est utilisé pour lire les données d’un fichier.
Créez un fichier texte avec le contenu suivant:
John 9
Johnnie 7
Jane 8
Joanna 7
my $data = slurp "datafile.txt";
say $data;
6.3.2. spurt
spurt
est utilisé pour écrire des données sur un fichier.
my $newdata = "New scores:
Paul 10
Paulie 9
Paulo 11";
spurt "newdatafile.txt", $newdata;
Après avoir exécuté le code ci-dessus, un nouveau fichier nommé newdatafile.txt sera créé. Il contiendra les nouveaux scores.
6.4. Travailler avec les fichiers et répertoires
Raku peut lister le contenu d’un répertoire sans exécuter des commandes shell (en utilisant ls
) comme nous l’avons vu dans un exemple précédent.
say dir; #Liste les fichiers et dossiers dans le répertoire courant
say dir "/Documents"; #Liste les fichiers et dossiers dans le répertoire spécifié
my @répertoire = dir; # Récupère les fichiers dans un tableau
De plus, vous pouvez créer de nouveaux dossiers et les supprimer.
mkdir "newfolder";
rmdir "newfolder";
mkdir
crée un nouveau répertoire.
rmdir
supprimer un répertoire vide. Renvoie une erreur s’il n’est pas vide.
Vous pouvez également vérifier si le chemin d’accès spécifié existe, si c’est un fichier ou un répertoire.
Dans le répertoire où vous allez exécuter le script ci-dessous, créez un dossier vide folder123
et un fichier Raku vide script123.raku
say "script123.raku".IO.e;
say "folder123".IO.e;
say "script123.raku".IO.d;
say "folder123".IO.d;
say "script123.raku".IO.f;
say "folder123".IO.f;
La méthode IO sert à transformer la chaîne de caractères « script123 » en un objet de type IO::Path . Les méthodes « e », « f » et « d » de tests de fichiers ne peuvent être invoquées que sur des objets de type IO::Path , d’où la nécessité de coercition préalable de la chaîne de caractères en un objet de ce type.
|
IO.e
vérifie si le répertoire/fichier existe.
IO.f
vérifie si c’est un fichier.
IO.d
vérifie si c’est un dossier.
Les utilisateurs Windows peuvent utiliser / ou \\ comme séparateur entre les dossiers:C:\\rakudo\\bin C:/rakudo/bin |
Pour plus d’informations sur les E/S, voir https://docs.raku.org/type/IO |
7. Routines
7.1. Définition
Les routines ou subroutines ou subs sont un moyen de conditionnement d’un ensemble de fonctionnalités.
Une routine est définie avec le mot-clef sub
. Après leur définition, elles peuvent être appelées par leur nom.
Examinez l’exemple ci-dessous:
sub salut-alien {
say "Bonjour Terriens";
}
salut-alien;
L’exemple précédent présente une routine qui ne nécessite aucun argument.
7.2. Signature
Beaucoup de routines requièrent des données en entrée pour fonctionner. Ces données sont fournies par des arguments.
La signature est le nombre et le type d’arguments que la routine accepte.
La routine ci-dessous accepte une chaîne pour argument:
sub dis-bonjour (Str $nom) {
say "Bonjour " ~ $nom ~ "!!!!"
}
dis-bonjour "Paul";
dis-bonjour "Paula";
7.3. Multiroutines
Il est possible de définir plusieurs routines ayant le même nom, mais des signatures différentes.
Lorsque la routine est appelée, l’environnement d’exécution décidera quelle version utiliser en fonction du nombre et du type des arguments fournis.
Ce type de routines est défini de la même manière que les routines normales sauf que nous utilisons le mot-clef multi
a la place de sub
.
multi salut($nom) {
say "Bonne Journée $nom";
}
multi salut($nom, $titre) {
say "Bonne Journée $titre $nom";
}
salut "Gaspard";
salut "Josiane","Mme.";
7.4. Arguments optionnels et par défaut
Si une routine est définie comme acceptant un argument, et nous l’appelons sans fournir l’argument requis, la routine va échouer.
Cependant, Raku nous offre la possibilité de définir des routines avec des:
-
Arguments optionnels
-
Arguments par défaut
Les arguments optionnels sont définis en ajoutant ?
après le nom de l’argument.
sub dis-bonjour($nom?) {
with $nom { say "Bonjour " ~ $nom }
else { say "Bonjour être humain" }
}
dis-bonjour;
dis-bonjour("Laura");
Si l’utilisateur ne fournit pas un argument, la routine peut fournir une valeur par défaut.
Cela se fait par l’attribution d’une valeur à l’argument durant la définition de la routine.
sub dis-bonjour($nom="Raoul") {
say "Bonjour " ~ $nom;
}
dis-bonjour;
dis-bonjour("Laura");
Pour plus d’informations sur les routines et fonctions, voir https://docs.raku.org/language/functions |
8. Programmation fonctionnelle
Ce chapitre traitera des fonctionnalités facilitant la programmation fonctionnelle.
8.1. Les fonctions sont des entités de première classe
Les fonctions/routines sont des entités de première classe:
-
elles peuvent être passées comme un argument
-
elles peuvent être renvoyées par une fonction
-
on peut les affecter à une variable
Un bon exemple pour vérifier ce concept est la fonction map
.
map
est une fonction d’ordre supérieur, elle accepte une autre fonction comme argument.
my @tableau = <1 2 3 4 5>;
sub carré($x) {
$x ** 2
}
say map(&carré,@tableau);
(1 4 9 16 25)
Nous avons défini une routine appelée carré
, qui met à la puissance 2 l’argument qui lui est passé.
Ensuite, nous avons utilisé map
, une fonction d’ordre supérieur en lui passant deux arguments, une routine et un tableau.
Le résultat est une liste de tous les éléments du tableau mis à la puissance 2.
Notez que quand une routine est passée comme argument, nous la préfixons avec &
.
8.2. Fermeture
Tous les objets code en Raku sont des fermetures, ce qui implique qu’ils peuvent référencer des variables lexicales d’une portée externe.
8.3. Fonctions anonymes
Une fonction anonyme est également appelée lambda.
Une fonction anonyme n’est pas liée à un identifiant (elle n’a pas de nom).
map
avec une fonction anonyme:my @tableau = <1 2 3 4 5>;
say map(-> $x {$x ** 2},@tableau);
Notez qu’au lieu de définir une routine et de la passer en argument à map
, nous la définissons directement à l’intérieur de map
.
La routine anonyme -> $x {$x ** 2}
n’a pas de nom et ne peut donc pas être appelée.
En dialecte Raku nous l’appelons un bloc pointu (pointy block).
my $carré = -> $x {
$x ** 2
}
say $carré(9);
8.4. Enchaînement
En Raku, les méthodes peuvent être enchaînées, vous n’avez plus à passer le résultat d’une méthode comme argument à une autre.
Supposons qu’on vous donne un tableau de valeurs. On vous demande de retourner les valeurs uniques de ce tableau en ordre décroissant.
Vous pouvez résoudre ce problème en écrivant quelque chose comme ceci:
my @tableau = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @tableau-final = reverse(sort(unique(@tableau)));
say @tableau-final;
Nous appelons d’abord la fonction unique
sur @tableau
puis nous passons le résultat comme argument à sort
et ensuite passons le résultat à reverse
.
L’exemple ci-dessus peut aussi être écrit comme suit, en prennant avantage de l’enchaînement des méthodes:
my @tableau = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @tableau-final = @tableau.unique.sort.reverse;
say @tableau-final;
Vous pouvez constater qu’enchaîner les méthodes est plus agréable à l’oeil et au cerveau.
8.5. Opérateur feed
L’opérateur feed, appelé Pipe dans d’autres langages fonctionnels, donne une meilleure vue de l’enchaînement de méthodes.
my @tableau = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@tableau ==> unique()
==> sort()
==> reverse()
==> my @tableau-final;
say @tableau-final;
Commence avec `@tableau` puis renvoie la liste des éléments uniques
puis effectue un tri
puis l'inverse
puis stocke le résultat dans @tableau-final
Comme vous le voyez, le flux des appels de méthodes se fait de haut en bas.
my @tableau = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @tableau-final-v2 <== reverse()
<== sort()
<== unique()
<== @tableau;
say @tableau-final-v2;
Le feed vers l’arrière est comme celui vers l’avant, mais se fait à rebours.
Le flux des appels de méthodes se fait de bas en haut.
8.6. Hyperopérateur
L' hyperpérateur >>.
invoque une méthode sur tous les éléments d’une liste et renvoie une liste des résultats.
my @tableau = <0 1 2 3 4 5 6 7 8 9 10>;
sub est-pair($var) { $var %% 2 };
say @tableau>>.is-prime;
say @tableau>>.&est-pair;
En utilisant l’hyperopérateur, nous pouvons appeler des méthodes déjà définies dans Raku, par exemple is-prime
qui nous indique si un nombre est premier ou pas.
Nous pouvons également définir de nouvelles routines et les appeler en utilisant l’hyperopérateur. En ce cas, il faut préfixer la méthode avec &
par exemple &est-pair
Cette façon de faire est très pratique, car elle nous évite d’écrire une boucle for
d’itéreration sur chaque valeur.
8.7. Jonctions
Une jonction est une superposition logique des valeurs.
Dans l’exemple ci-dessous, 1|2|3
est une jonction.
my $var = 2;
if $var == 1|2|3 {
say "La variable est soit 1 ou 2 ou 3";
}
L’utilisation de jonctions déclenche généralement l'autothreading; l’opération est effectuée pour chaque élément de la jonction, les résultats sont combinés en une seule jonction et renvoyés.
8.8. Listes paresseuses
Une liste paresseuse est une liste dont l’évaluation peut être retardée.
L’évaluation paresseuse diffère l’évaluation d’une expression jusqu’au momment où celle-ci est necessaire, et evite ainsi la répétition des évaluations en stockant les résultats dans une table de correspondance.
Les avantages, parmi d’autres, sont les suivants:
-
Un gain de performance évitant les calculs inutiles
-
La possibilité de construire des structures de données potentiellement infinies
-
La possibilité de définir une structure de contrôle
Pour construire une liste paresseuse on utilise l’opérateur infixé …
Une liste paresseuse possède un ou des éléments initiaux, un générateur, et un élément final.
my $lazylist = (1 ... 10);
say $lazylist;
L’élément initial est 1, et l’élément final est 10. Aucun générateur n’a été défini donc le générateur par défaut se fait par sucession (+1)
Autrement dit, cette liste paresseuse retournera (à la demande) les éléments suivants: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
my $lazylist = (1 ... Inf);
say $lazylist;
Cette liste retournera (à la demande) les entiers entre 1 et l’infini, c’est-à-dire tous les entiers.
my $lazylist = (0,2 ... 10);
say $lazylist;
Les éléments initiaux sont 0 et 2, et le point final est 10.
Aucun générateur n’est défini, mais en utilisant les éléments initiaux, Raku déduira que le générateur est (+2)
Cette liste paresseuse retournera (à la demande) les éléments suivants: (0, 2, 4, 6, 8, 10)
my $lazylist = (0, { $_ + 3 } ... 12);
say $lazylist;
Dans cet exemple, nous définissons explicitement un générateur mis entre { }
Cette liste paresseuse retournera (à la demande) les éléments suivants: (0, 3, 6, 9, 12)
Si vous utilisez un générateur explicite, l’élément final doit être une valeur que le générateur puisse retourner. Vous pouvez sinon remplacer Ceci ne stopera pas le générateur
Ceci stopera le générateur
|
9. Classes & Objets
Dans le chapitre précédent nous avons vu comment Raku facilite la programmation fonctionnelle.
Dans ce chapitre nous allons aborder la programmation orientée objet en Raku.
9.1. Introduction
La programmation Orientée Objet est l’un des paradigmes les plus utilisés de nos jours.
Un objet est une collection de variables et routines empaquetées ensemble.
Les variables sont appelées des attributs et les routines des méthodes.
Les attributs définissent l'état et les méthodes le comportement d’un objet.
Une classe définit la structure d’une collection d'objets.
Pour comprendre cette relation, examinez l’exemple ci-dessous:
Il y a 4 personnes présentes dans une pièce |
objets ⇒ 4 personnes |
Ces 4 personnes sont des êtres humains |
classe ⇒ Humain |
Ils ont des noms, ages, sexes et nationalités différents |
attributs ⇒ nom, age, sexe, nationalité |
Dans le jargon orienté objet, nous disons que les objets sont des instances d’une classe.
Voyez le script ci-dessous:
class Humain {
has $nom;
has $age;
has $sexe;
has $nationalité;
}
my $françois;
$françois = Humain.new(nom => 'François', age => 23, sexe => 'M', nationalité => 'Sarthoise');
say $françois;
Le mot-clef class
est utilisé pour définir une classe.
Le mot-clef has
est utilisé pour définir un attribut d’une classe.
La méthode .new()
est appelée un constructeur. Elle crée l’objet comme une instance de la classe sur laquelle elle a été appelée.
Dans le script ci-dessus, la nouvelle variable $françois
contient une référence vers une nouvelle instance de "Humain" définie par Humain.new()
.
Les arguments passés à la méthode .new()
sont utilisés pour initialiser les attributs de l’objet.
Une classe peut se voir donner une portée lexicale en utilisant my
:
my class Humain {
}
9.2. Encapsulation
L’encapsulation est un concept orienté objet qui rassemble une collection de données et de méthodes.
Les données (attributs) dans un objet se doivent d’être privées, c’est-à-dire qu’elles ne sont accessibles qu’à l’intérieur de l’objet.
Pour avoir accès aux attributs depuis l’extérieur de l’objet, on utilise des méthodes appelées accesseurs.
Les deux scripts ci-dessous ont le même résultat.
my $var = 7;
say $var;
my $var = 7;
sub disvar {
$var;
}
say disvar;
La méthode disvar
est un accesseur. Elle nous permet d’avoir accès à la valeur de la variable sans avoir d’accès direct à celle-ci.
L’encapsulation est facilitée en Raku par l’emploi de twigils.
Les twigils sont des sigils secondaires. Ils s’intercalent entre le sigil et le nom de l’attribut.
On utilise 2 twigils dans les classes:
-
!
est utilisé pour déclarer explicitement qu’un attribut est privé. -
.
est utilisé pour générer automatiquement un accesseur pour un attribut.
Par défaut, tout les attributs sont privés, mais c’est une saine habitude que d’utiliser le twigil !
.
En accord avec ce qui vient d’être dit, nous devrions réécrire la classe comme suit:
class Humain {
has $!nom;
has $!age;
has $!sexe;
has $!nationalité;
}
my $françois = Humain.new(nom => 'François', age => 23, sexe => 'M', nationalité => 'Sarthoise');
say $françois;
Si vous ajoutez à ce script la ligne suivante: say $françois.age;
L’erreur suivante sera retournée: Method 'age' not found for invocant of class 'Humain'
La raison en est que $!age
est privé et ne peut être utilisé que dans l’objet.
Y accéder en dehors de l’objet retournera une erreur.
Maintenant remplacez has $!age
par has $.age
et voyez quel est le résultat de say $françois.age;
9.3. Arguments nommés vs. positionnels
En Raku, toutes les classes héritent d’un constructeur par défaut .new()
.
Il peut être utilisé pour créer des objets en leur fournissant des arguments.
Le constructeur par défaut ne peut se voir fournir que des arguments nommés.
Si vous regardez l’exemple ci-dessus, vous voyez que les arguments passés à .new()
sont définis par leur noms:
-
nom => 'François'
-
age => 23
Et si je ne veux pas passer le nom de chaque attribut à chaque fois que je crée un nouvel objet?
Je dois alors créer un autre constructeur qui accepte les arguments positionnels.
class Humain {
has $.nom;
has $.age;
has $.sexe;
has $.nationalité;
#nouveau constructeur qui prime sur constructeur par défaut.
method new ($nom,$age,$sexe,$nationalité) {
self.bless(:$nom,:$age,:$sexe,:$nationalité);
}
}
my $françois = Humain.new('François',23,'M','Sarthoise');
say $françois;
Le constructeur qui accepte des arguments positionnels doit être défini comme ci-dessus.
9.4. Méthodes
9.4.1. Introduction
Les méthodes sont les routines d’un objet.
Comme les routines, elles sont un moyen d’empaqueter une collection de fonctionnalités, elles acceptent des arguments, ont une signature et peuvent être définies comme multi.
Les méthodes sont définies en utilisant le mot-clef method
.
Dans leur usage courant, les méthodes sont requises pour effectuer une action sur les attributs des objets.
Ce qui applique l’idée d’encapsulation. Les attributs des objets ne peuvent être manipulés qu’à l’intérieur de l’objet en utilisant des méthodes.
L’environement extérieur ne peut interargir avec l’objet qu’à travers ses méthodes, et n’a pas accès à ses attributs.
class Humain {
has $.nom;
has $.age;
has $.sexe;
has $.nationalité;
has $.éligible;
method confirme-éligibilité {
if self.age < 21 {
$!éligible = 'Non'
} else {
$!éligible = 'Oui'
}
}
}
my $françois = Humain.new(nom => 'John', age => 23, sexe => 'M', nationalité => 'Sarthoise');
$françois.confirme-éligibilité;
say $françois.éligible;
Une fois les méthodes définies dans une classe, elles peuvent être appelées sur un objet en utilisant l’opérateur point:
objet . méthode ou comme dans l’exemple ci-dessus: $françois.confirme-éligibilité
Dans la définition d’une méthode, si nous avons besoin de faire référence à l’objet en soi pour appeler une autre méthode, on utilise le mot-clef self
.
Dans la définition d’une méthode, si nous avons besoin de faire référence à un attribut, on utilise !
même si il a été définit avec .
La raison en est que le twigil .
déclare un attribut privé (avec !
) et automatise ensuite la création d’un accesseur ayant le même nom.
Dans l’exemple ci-dessus if self.age < 21
et if $!age < 21
auraient le même résultat, bien qu’ils soient techniquement différents:
-
self.age
appelle la méthode.age
(accesseur)
On peut aussi l’écrire$.age
-
$!age
est un appel direct à la variable.
9.4.2. Méthodes privées
Les méthodes normales peuvent être appelées sur des objets depuis l’extérieur de la classe.
Les méthodes privées sont des méthodes qui ne peuvent être appelées qu’à l’intérieur de la classe.
Un cas possible d’usage serait une méthode qui en appelle une autre pour une action spécifique.
La méthode qui communique avec l’extérieur est publique alors que celle qui est référencée doit rester privée.
Nous ne voulons par que les utilisateurs l’appellent directement, on la déclare donc comme privée.
La déclaration d’une méthode privée nécessite l’emploi du twigil !
avant son nom.
Les méthodes privées sont appelées avec !
à la place de .
method !jesuisprivée {
#du code ici
}
method jesuispublique {
self!jesuisprivée;
#faire d'autres choses
}
9.5. Attributs de classe
Les attributs de classe sont des attributs qui appartiennent à la classe, mais pas à ses objets.
Ils peuvent être initialisés pendant la définition.
Les attributs de classe sont déclarés en utilisant my
au lieu de has
.
Ils sont appelés sur la classe elle-même au lieu de ses objets.
class Humain {
has $.nom;
my $.compteur = 0;
method new($nom) {
Humain.compteur++;
self.bless(:$nom);
}
}
my $a = Humain.new('a');
my $b = Humain.new('b');
say Humain.compteur;
9.6. Types d’accès
Jusqu’à présent, tous les exemples que nous avons vus utilisent des accesseurs pour obtenir des informations sur les attributs de l’objet.
Et si nous avons besoin de modifier la valeur d’un attribut,
nous devons marquer cet attribut comme lisible/enregistrable en utilisant le mot-clef is rw
class Humain {
has $.nom;
has $.age is rw;
}
my $françois = Humain.new(nom => 'François', age => 21);
say $françois.age;
$françois.age = 23;
say $françois.age;
Par défaut, tous les attribut sont déclarés en lecture seule mais vous pouvez aussi les déclarer explicitement avec is readonly
9.7. Héritage
9.7.1. Introduction
L'héritage est un autre concept de la programmation orientée objet.
Quand on définit des classes, on se rend compte que ces classes ont certains attributs/méthodes communs.
Devons nous dupliquer le code?
NON! Il faudrait utiliser l'héritage.
Disons que nous voulons créer deux classes, une pour les humains et une pour les employés.
Les humains ont deux attributs: nom et âge.
Les employés ont quatre attributs: nom, âge, société et salaire.
On pourrait être tempté de définir les classes comme suit:
class Humain {
has $.nom;
has $.age;
}
class Employé {
has $.nom;
has $.age;
has $.société;
has $.salaire;
}
Bien que correcte, la part de code ci-dessus est conceptuellement maladroite.
Une meilleure approche serait:
class Humain {
has $.nom;
has $.age;
}
class Employé is Humain {
has $.société;
has $.salaire;
}
Le mot-clef is
définit l’héritage.
Dans le Jargon orienté objet, on dit que Employé est un enfant (ou une classe fille) de Humain, et Humain est un parent d’Employé.
Toutes les classes filles héritent des attributs et des méthodes de la classe parente, il est donc inutile de les redéfinir.
9.7.2. Surcharge
Les classes héritent de tous les attributs et méthodes de leur classe parente.
Dans certains cas, nous avons besoin que la méthode d’un classe fille se comporte différemment de celle dont elle hérite.
Afin d’obtenir ce comportement, nous redéfinisson la méthode dans la classe fille.
Ce concept est nommé surcharge.
Dans l’exemple ci-dessous, la classe Employé hérite de la méthode présentez-vous
.
class Humain {
has $.nom;
has $.age;
method présentez-vous {
say "Bonjour je suis un être humain, et je m'appelle " ~ self.nom;
}
}
class Employé is Humain {
has $.société;
has $.salaire;
}
my $françois = Humain.new(nom => 'François', age => 23,);
my $anne = Employé.new(nom => 'Anne', age => 25, société => 'Acme', salaire => 2000);
$françois.présentez-vous;
$anne.présentez-vous;
La surcharge se fait comme ceci:
class Humain {
has $.nom;
has $.age;
method présentez-vous {
say "Bonjour je suis un être humain, et je m'appelle " ~ self.nom;
}
}
class Employé is Humain {
has $.société;
has $.salaire;
method présentez-vous {
say "Bonjour je suis un être humain, et je m'appelle " ~ self.nom ~ ' et je travaille chez: ' ~ self.société;
}
}
my $françois = Humain.new(nom => 'François', age => 23,);
my $anne = Employé.new(nom => 'Anne', age => 25, société => 'Acme', salaire => 2000);
$françois.présentez-vous;
$anne.présentez-vous;
Suivant la classe dans laquelle se trouve l’objet, la bonne méthode sera appelée.
9.7.3. Sous-méthodes
Les Sous-méthodes sont un type de méthode dont les classes filles n’héritent pas.
Elles ne sont accessibles que depuis la classe dans laquelle elles sont déclarées.
Elles sont déclarées en utilisant le mot-clef submethod
.
9.8. Héritage multiple
L’héritage multiple est disponible en Raku. Une classe peut hériter de plusieurs autres classes.
class barre-graph {
has Int @.barre-valeurs;
method dessiner {
say @.barre-valeurs;
}
}
class ligne-graph {
has Int @.ligne-valeurs;
method dessiner {
say @.ligne-valeurs;
}
}
class combo-graph is barre-graph is ligne-graph {
}
my $ventes-réelles = barre-graph.new(barre-valeurs => [10,9,11,8,7,10]);
my $ventes-prévisions = ligne-graph.new(ligne-valeurs => [9,8,10,7,6,9]);
my $réelles-vs-prévisions = combo-graph.new(barre-valeurs => [10,9,11,8,7,10],
ligne-valeurs => [9,8,10,7,6,9]);
say "Ventes Réelles:";
$ventes-réelles.dessiner;
say "Ventes Prévisionelles:";
$ventes-prévisions.dessiner;
say "Réelles vs Prévisionelles:";
$réelles-vs-prévisions.dessiner;
Sortie
Ventes Réelles:
[10 9 11 8 7 10]
Ventes Prévisionelles:
[9 8 10 7 6 9]
Réelles vs Prévisionelles:
[10 9 11 8 7 10]
La classe combo-graph
doit être capable de contenir deux séries, une pour les valeurs réelles dessinées sous forme de barres
et une autre pour les valeurs prévisionelles dessinées sous forme de ligne.
C’est pourquoi nous l’avons définie comme fille de ligne-graph
et barre-graph
Vous avez remarqué que l’appel de la méthode dessiner
sur combo-graph
n’a pas rendu les bons résultats.
Une seule série a été dessinée.
Que c’est-il passé?
combo-graph
hérite de barre-graph
et de ligne-graph
; et chaque parent possède un méthode appelée dessiner
.
Quand nous appelons cette méthode sur combo-graph
Raku résoudra le conflit en appelant une des méthodes héritées.
Pour avoir un comportement valide, nous devons surcharger la méthode dessiner
dans combo-graph
.
class barre-graph {
has Int @.barre-valeurs;
method dessiner {
say @.barre-valeurs;
}
}
class ligne-graph {
has Int @.ligne-valeurs;
method dessiner {
say @.ligne-valeurs;
}
}
class combo-graph is barre-graph is ligne-graph {
method dessiner {
say @.barre-valeurs;
say @.ligne-valeurs;
}
}
my $ventes-réelles = barre-graph.new(barre-valeurs => [10,9,11,8,7,10]);
my $ventes-prévisions = ligne-graph.new(ligne-valeurs => [9,8,10,7,6,9]);
my $réelles-vs-prévisions = combo-graph.new(barre-valeurs => [10,9,11,8,7,10],
ligne-valeurs => [9,8,10,7,6,9]);
say "Ventes Réelles:";
$ventes-réelles.dessiner;
say "Ventes Prévisionelles:";
$ventes-prévisions.dessiner;
say "Réelles vs Prévisionelles:";
$réelles-vs-prévisions.dessiner;
Sortie
Ventes Réelles:
[10 9 11 8 7 10]
Ventes Prévisionelles:
[9 8 10 7 6 9]
Réelles vs Prévisionelles:
[10 9 11 8 7 10]
[9 8 10 7 6 9]
9.9. Rôles
Les rôles sont assimilables à des classes, dans le sens qu’ils sont une collection d’attributs et de méthodes.
Les rôles sont déclarés avec le mot-clef role
, les classes qui veulent implémenter un rôle peuvent le faire en utilisant le mot-clef does
.
role barre-graph {
has Int @.barre-valeurs;
method dessiner {
say @.barre-valeurs;
}
}
role ligne-graph {
has Int @.ligne-valeurs;
method dessiner {
say @.ligne-valeurs;
}
}
class combo-graph does barre-graph does ligne-graph {
method dessiner {
say @.barre-valeurs;
say @.ligne-valeurs;
}
}
my $ventes-réelles = barre-graph.new(barre-valeurs => [10,9,11,8,7,10]);
my $ventes-prévisions = ligne-graph.new(ligne-valeurs => [9,8,10,7,6,9]);
my $réelles-vs-prévisions = combo-graph.new(barre-valeurs => [10,9,11,8,7,10],
ligne-valeurs => [9,8,10,7,6,9]);
say "Ventes Réelles:";
$ventes-réelles.dessiner;
say "Ventes Prévisionelles:";
$ventes-prévisions.dessiner;
say "Réelles vs Prévisionelles:";
$réelles-vs-prévisions.dessiner;
Si vous lancez le sript ci-dessus, vous constatez que les résultats sont les mêmes.
Vous vous demandez maintenant: si les rôles se comportent comme des classes, quelle est leur utilité?
Pour répondre à votre question, modifiez le premier script utilisé pour illustrer l’héritage multiple,
celui où nous avons oublié de surclasser la méthode dessiner
.
role barre-graph {
has Int @.barre-valeurs;
method dessiner {
say @.barre-valeurs;
}
}
role ligne-graph {
has Int @.ligne-valeurs;
method dessiner {
say @.ligne-valeurs;
}
}
class combo-graph does barre-graph does ligne-graph {
}
my $ventes-réelles = barre-graph.new(barre-valeurs => [10,9,11,8,7,10]);
my $ventes-prévisions = ligne-graph.new(ligne-valeurs => [9,8,10,7,6,9]);
my $réelles-vs-prévisions = combo-graph.new(barre-valeurs => [10,9,11,8,7,10],
ligne-valeurs => [9,8,10,7,6,9]);
say "Ventes Réelles:";
$ventes-réelles.dessiner;
say "Ventes Prévisionelles:";
$ventes-prévisions.dessiner;
say "Réelles vs Prévisionelles:";
$réelles-vs-prévisions.dessiner;
Sortie
===SORRY!===
Method 'dessiner' must be resolved by class combo-graph because it exists in multiple roles (ligne-graph, barre-graph)
Si plusieurs rôles sont appliqués à la même classe, et un conflit survient, une erreur de compilation sera lancée.
Ceci est une approche plus sûre que l’héritage multiple, où les conflits ne sont pas pris comme des erreurs et sont résolus à l’exécution.
Les rôles vous avertirons en cas de conflit.
9.10. Introspection
L'introspection sert à obtenir des informations sur les propriétés d’un objet: comme son type, ses attributs ou ses méthodes.
class Humain {
has $.nom;
has $.age;
method présentez-vous {
say "Bonjour je suis un être humain, et je m'appelle " ~ self.nom;
}
}
class Employé is Humain {
has $.société;
has $.salaire;
}
my $françois = Humain.new(nom => 'François', age => 23,);
my $anne = Employé.new(nom => 'Anne', age => 25, société => 'Acme', salaire => 2000);
$françois.présentez-vous;
$anne.présentez-vous;
say $françois.WHAT;
say $anne.WHAT;
say $françois.^attributes;
say $anne.^attributes;
say $françois.^methods;
say $anne.^methods;
say $anne.^parents;
if $anne ~~ Humain {say 'anne est un être humain'};
L’introspection est facilitée par:
-
.WHAT
renvoie la classe depuis laquelle l’objet a été créé. -
.^attributes
renvoie une liste qui contient tous les attributs de l’objet. -
.^methods
renvoie toutes les méthodes implémentables de l’objet. -
.^parents
renvoie toutes les classes parentes de la classe à laquelle appartient l’objet. -
~~
est appelé l’opérateur de correspondance intelligente (smart-match). Il renvoie Vrai si l’objet correspond à sa classe de création ou à une de celles dont elle a hérité.
10. Gestion des exceptions
10.1. Capture des Exceptions
Les exceptions son un comportement particulier qui intervient quand quelque chose se passe mal à l’execution.
On dit que les exceptions sont lancées ou générées.
Le script ci-dessous, qui s’exécute correctement:
my Str $nom;
$nom = "Josiane";
say "Bonjour " ~ $nom;
say "Comment ça va?"
Sortie
Bonjour Josiane
Comment ça va?
Maintenant, voici un script analogue qui lance une exception:
my Str $nom;
$nom = 123;
say "Bonjour " ~ $nom;
say "Comment ça va?"
Sortie
Type check failed in assignment to $nom; expected Str but got Int (123)
Vous remarquerez que quand une erreur survient (ici affecter un nombre à une variable de chaîne), le programme s’arrête et les lignes suivantes ne seront pas évaluées, même si elles sont correctes.
La gestion des exceptions capture une exception qui a été lancée afin que le script puisse continuer à fonctionner.
my Str $nom;
try {
$nom = 123;
say "Hello " ~ $nom;
CATCH {
default {
say "Pouvez vous nous redonner votre nom, nous ne l'avons pas trouvé dans le registre.";
}
}
}
say "Comment ça va?";
Sortie
Pouvez vous nous redonner votre nom, nous ne l'avons pas trouvé dans le registre.
Comment ça va?
La gestion d’exception se fait en utilisant un bloc try-CATCH
.
try {
#le code va ici
#si quelque chose se passe mal le script entre dans le bloc CATCH
#si tout se passe bien, le bloc CATCH sera ignoré
CATCH {
default {
#le code présent ici ne sera évalué que si une exception a été lancée
}
}
}
Un bloc CATCH
peut se définir de la même façon qu’un bloc given
.
Ce qui implique qu’on puisse faire un catch sur différent types d’exceptions.
try {
#le code va ici
#si quelque chose se passe mal le script entre dans le bloc CATCH
#si tout se passe bien, le bloc CATCH sera ignoré
CATCH {
when X::AdHoc { #faire quelque chose si une exception de type X::AdHoc est lancée }
when X::IO { #faire quelque chose si une exception de type X::IO est lancée }
when X::OS { #faire quelque chose si une exception de type X::OS est lancée }
default { #faire quelque chose si une exception est lancée qui ne correspond pas aux types précédents }
}
}
10.2. Lancer des exceptions
A l’inverse de capturer les exceptions, Raku vous permet aussi d’en lancer.
On peut lancer deux types d’exceptions:
-
les exceptions ad-hoc
-
les exceptions typées
my Int $age = 21;
die "Erreur !";
my Int $age = 21;
X::AdHoc.new(payload => 'Erreur !').throw;
Les exceptions ad hoc sont lancées en utilisant la routine die
suivie du message de l’exception.
Les exceptions typées sont des objets, d’où l’utilisation du constructeur .new()
dans l’exemple ci-dessus.
Toutes les exceptions déscendent de la classe X
, quelques exemples ci-dessous:
X::AdHoc
est le type le plus simple d’exception
X::IO
est lié aux erreurs IO (entrées/sorties)
X::OS
est lié aux erreurs OS (système)
X::Str::Numeric
est lié aux erreurs de conversion d’une chaîne vers un nombre.
Pour une liste complète des types d’exceptions et une liste de leurs méthodes associées, allez sur https://docs.raku.org/type.html et naviguez dans les types qui commencent par X. |
11. Expressions régulières
Une expression régulières, ou regex est une séquence de caractères utilisée pour le filtrage par motif.
La méthode la plus simple pour concevoir ce système, est d’y penser comme un motif qui peut être répété/isolé/extrait.
if 'ensoleillement' ~~ m/ soleil / {
say "ensoleillement contient le mot soleil";
}
Dans cet exemple l’opérateur de correspondance intelligent (smart-match) ~~
est utilisé pour vérifier si une chaîne de caractères (ensoleillement) contient le mot (soleil).
"ensoleillement" est comparé à la regex m/ soleil /
.
11.1. Définition d’une regex
Une expression régulière peut être définie comme suit:
-
/soleil/
-
m/soleil/
-
rx/soleil/
Les espaces, sauf si spécifiés comme requis explicitement, ne sont pas pris en compte: m/soleil/
and m/ soleil /
sont identiques.
11.2. Correspondance par caractères
Les caractères alphanumériques et le tiret bas _
sont utilisés tels quels.
Tous les autres caractères doivent être "protégés" en les préfixant de la barre oblique inversée, ou mis entre guillemets (simples ou doubles).
if 'Température: 13' ~~ m/ \: / {
say "La chaîne fournie contient le caractère deux-points :";
}
if 'Age = 13' ~~ m/ '=' / {
say "La chaîne fournie contient le caractère égal = ";
}
if 'nom@société.com' ~~ m/ "@" / {
say "Cette adresse mail semble valide car elle contient un caractère @";
}
11.3. Comparaison par catégories de caractères
Les caractères peuvent être réunis en catégories de reconnaissances.
On peut également utiliser la négation d’une catégorie (tout sauf cette catégorie):
Catégorie |
Regex |
Inverse |
Regex |
Caractère de mot (lettre, chiffre, ou tiret bas) |
\w |
Tout caractère sauf caractère de mot |
\W |
Chiffre |
\d |
Tout caractère sauf un chiffre |
\D |
Espace |
\s |
Tout caractère sauf espace |
\S |
Espace horizontal |
\h |
Tout caractère sauf espace Horizontal |
\H |
Espace vertical |
\v |
Tout caractère sauf espace vertical |
\V |
Tabulation |
\t |
Tout caractère sauf tabulation |
\T |
Saut de ligne |
\n |
Tout caractère sauf saut de ligne |
\N |
if "Robert123" ~~ / \d / {
say "Pas de comparaison valide car pas de chiffres";
} else {
say "Comparaison valide, car un chiffre"
}
if "John-Doe" ~~ / \s / {
say "Cette chaîne contient un espace";
} else {
say "Cette chaîne ne contient pas d'espace"
}
11.4. Propriétés unicode
La section précédente montre l’utilité d’utiliser des catégories de caractères.
Ceci dit, une approche plus systématique pourrait être d’utiliser les propriétés Unicode.
Les propriétés Unicode sont notées à l’intérieur de <: >
if "Robert123" ~~ / <:N> / {
say "Contient un chiffre";
} else {
say "Ne contient pas un chiffre"
}
if "Albert-Ebasque" ~~ / <:Lu> / {
say "Contient une lettre en majuscule";
} else {
say "Ne contient de lettre en majuscule"
}
if "Albert-Ebasque" ~~ / <:Pd> / {
say "Contient un tiret";
} else {
say "Ne contient pas un tiret"
}
11.5. Métacaractères
Les métacaractères (wildcards) peuvent également être utilisés dans les regex.
Le point .
signifie n’importe quel caractère unique.
if 'abc' ~~ m/ a.c / {
say "Correspondance";
}
if 'a2c' ~~ m/ a.c / {
say "Correspondance";
}
if 'ac' ~~ m/ a.c / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
11.6. Quantificateurs
Les quantificateurs viennent après un caractère et sont utilisés pour déterminer le nombre de fois qu’il doit apparaître.
Le point d’interrogation ?
signifie zéro ou une fois.
if 'ac' ~~ m/ a?c / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'c' ~~ m/ a?c / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
L’astérisque *
signifie zéro ou plusieurs fois.
if 'az' ~~ m/ a*z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'aaz' ~~ m/ a*z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'z' ~~ m/ a*z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
Le +
signifie au moins une fois.
if 'az' ~~ m/ a+z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'aaz' ~~ m/ a+z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
if 'z' ~~ m/ a+z / {
say "Correspondance";
} else {
say "Pas de Correspondance";
}
11.7. Résultats de correspondance
Quand la mise en correspondance avec une regex est positive,
le résultat est stocké dans la variable spéciale $/
if 'Rakudo est un compilateur Raku' ~~ m/compilateur/ {
say "La correspondance est: " ~ $/;
say "La chaîne avant la correspondance est: " ~ $/.prematch;
say "La chaîne après la correspondance est: " ~ $/.postmatch;
say "La chaîne reconnue commence à la position: " ~ $/.from;
say "La chaîne reconnue finit à la position: " ~ $/.to;
}
La correspondance est: compilateur
La chaîne avant la correspondance est: Rakudo est un
La chaîne après la correspondance est: Raku
La chaîne reconnue commence à la position: 14
La chaîne reconnue finit à la position: 25
$/
renvoie un Objet de Correspondance (Match Object), c’est-à-dire la chaîne qui correspond à la regex
Les méthodes suivantes peuvent être appelées sur l'Objet de Correspondance:
.prematch
renvoie la chaîne qui précède la correspondance.
.postmatch
renvoie la chaîne qui suit la correspondance.
.from
renvoie la position de départ de la correspondance.
.to
renvoie la position de fin de la correspondance.
Par défaut le caractère espace n’est pas pris en compte. Si nous voulons faire une correspondance avec une regex contenant un espace nous devons le définir explicitement. Le :s dans la regex m/:s Raku 楽/ force l’espace à être considéré et non ignoré.D’une autre façon, nous aurions pu écrire la regex comme ceci: m/ Perl\s楽/ en utilisant comme vu précédemment \s comme indicateur d’un espace.Si une regex contient plusieurs espaces, utiliser :s devient plus efficace que l’utilisation de \s pour chaque espace.
|
11.8. Exemple
Vérifions si une adresse mail est valide ou non.
Pour la pertinence de cet exemple nous conviendrons qu’une adresse mail est valide si elle est formée comme suit:
prénom [point] nom [arobase] société [point] (com/org/net)
La regex utilisée ici pour la validation de mail est très insuffisante. Elle ne sert ici qu’à titre d’exemple pour demontrer les fonctionnalités de regex de Raku. Ne pas l’utiliser telle quelle en production. |
my $email = 'sam.suffit@raku.org';
my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /;
if $email ~~ $regex {
say $/ ~ " est une adresse mail valide";
} else {
say "n'est pas une adresse mail valide";
}
sam.suffit@raku.org est une adresse mail valide
<:L>
correspond à une lettre
<:L>` correspond à une lettre ou plus +
`\.` correspond à un caractère [point] +
`\@` correspond a un caractère [arobase] +
`<:L:N>
correspond à une lettre ou un chiffre
<:L+:N>+
correspond à une lettre ou un chiffre, plusieurs fois
La regex peut être décomposée comme suit:
-
nom
<:L>+
-
[point]
\.
-
prénom
<:L>+
-
[arobase]
\@
-
nom de la société
<:L+:N>+
-
[point]
\.
-
com/org/net
<:L>+
my $email = 'sam.suffit@raku.org';
my regex lettres { <:L>+ };
my regex point { \. };
my regex arobase { \@ };
my regex lettres-et-chiffres { <:L+:N>+ };
if $email ~~ / <lettres> <point> <lettres> <arobase> <lettres-et-chiffres> <point> <lettres> / {
say $/ ~ " est une adresse mail valide";
} else {
say "Ce n'est pas une adresse mail valide";
}
Une regex nommée est définie en utilisant la syntaxe suivante: my regex nom-de-la-regex { définition de la regex }
Une regex nommée peut être appelée en utilisant la syntaxe suivante: <nom-de-la-regex>
Pour plus de détails sur les regexes, voir https://docs.raku.org/language/regexes |
12. Modules Raku
Raku est un langage à but générique. Il peut être utilisé pour remplir plusieurs taches, incluant: traitement d’un texte, image, web, bases de données, protocoles réseau, etc.
La réusabilité est un concept très important qui permet aux programmeurs de ne pas réinventer la roue à chaque nouvelle tache.
Raku permet la création et la redistribution de modules. Chaque module donne une série de fonctionnalités empaquetées, qui une fois installées, peuvent être réutilisées.
Zef et un outil de gestion de modules livré avec Rakudo.
Pour installer un module spécifique, taper la commande suivante dans votre terminal:
zef install "nom du module"
Une liste des modules Raku se trouve ici: https://modules.raku.org |
12.1. Utiliser les modules
MD5 est une fonction de hachage cryptographique qui produit une valeur de hachage de 128-bit.
MD5 a de nombreuses utilités, dont le chiffrage des mots de passe stockés dans une base de données.
Quand un nouvel utilisateur s’inscrit, ses références ne sont pas stockées en texte brut, mais sous forme de hachage.
L’idée derrière tout ceci est que si la base de données est compromise, l’attaquant ne pourra pas identifier les mots de passe.
Disons que vous ayez besoin d’un script qui génère un hachage MD5 sur un mot de passe avant stockage dans la base de données.
Heureusement, Raku possède un module qui implémente l’algorithme de hachage MD5. Installons-le:
zef install Digest::MD5
Maintenant, lançons le script suivant:
use Digest::MD5;
my $secret = "secret123";
my $secret-h = Digest::MD5.new.md5_hex($secret);
say $secret-h;
Pour lancer la fonction md5_hex()
qui crée les hachages, nous devons charger le module voulu.
Le mot-clef use
charge le module pour son utilisation dans le script.
Dans la pratique, le hachage MD5 n’est pas suffisant, car vulnérable aux attaques par dictionnaire. Il doit être combiné avec un "salage" https://fr.wikipedia.org/wiki/Salage_(cryptographie). |
13. Unicode
Unicode est un standard informatique de représentation du texte, qui prend en compte la la plupart des systèmes d’écriture dans le monde.
UTF-8 est un encodage de caractères, capable d’encoder tous les caractères (ou "Points de code") en Unicode.
Les caractères sont définis par un:
Graphème: Représentation visuelle.
Point de code: Un nombre assigné à un caractère.
13.1. Utiliser Unicode
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";
Les trois lignes ci-dessus montrent les différentes façons de construire un caractère:
-
Ecriture directe (graphème)
-
Utilisation de
\x
suivit du point de code -
Utilisation de
\c
et du nom du point de code
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";
La lettre á
peut être écrite:
-
en utilisant son point de code unique
\x00e1
-
ou comme la combinaison des points de code de:
a
et de l’accent aigu\x0061\x0301
say "á".NFC;
say "á".NFD;
say "á".uniname;
Output
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE
NFC
renvoie le point code unique.
NFD
décompose le caractère et renvoie ses points de code.
uniname
renvoie le nom du point de code.
my $Δ = 1;
$Δ++;
say $Δ;
14. Parallélisme, Concurrence et Asynchronisme
14.1. Parallélisme
En temps normal, toutes les taches d’un programme s’effectuent de façon séquentielle. Ce n’est pas un grand problème si le temps d’exécution n’est pas crucial pour vous.
Raku intègre de façon naturelle le lancement de taches en parallèle.
À ce point, il est important de considérer que le parallélisme peut vouloir dire deux choses:
-
Parallélisme des taches: Deux (ou plus) expressions indépendantes lancées en parallèle.
-
Parallélisme des données: Un expression unique itérée sur une liste d’éléments en parallèle.
Commençons par ce dernier.
14.1.1. Parallélisme des données
my @tableau = (0..50000); #Remplissage du tableau
my @résultat = @tableau.map({ is-prime $_ }); #Appel de is-prime sur chaque élément
say now - INIT now; #Temps d'exécution du programme
Nous ne faisons qu’une seule opération @array.map({ is-prime $_ })
La sous-routine is-prime
est appelée séquentiellement pour chaque élément du tableau:
is-prime @tableau[0]
puis is-prime @tableau[1]
puis is-prime @tableau[2]
etc.
is-prime
sur plusieurs éléments en même temps:my @tableau = (0..50000); #Remplissage du tableau
my @résultat = @tableau.race.map({ is-prime $_ }); #Appel de is-prime sur chaque élément
say now - INIT now; #Temps d'exécution du programme
Notez l’emploi de race
dans l’expression.
Cette méthode permet l’itération en parallèle des éléments du tableau.
Après avoir lancé les deux exemples (avec et sans race
), comparez les temps d’exécution de chaque programme.
race
hyper
En lançant les deux exemples, vous remarquerez que l’un est trié et l’autre non. |
14.1.2. Parallélisme des tâches
my @tab1 = (0..49999);
my @tab2 = (2..50001);
my @résultat1 = @tab1.map( {is-prime($_ + 1)} );
my @résultat2 = @tab2.map( {is-prime($_ - 1)} );
say @résultat1 eqv @résultat2;
say now - INIT now;
-
Nous avons défini deux tableaux
-
Appliqué une opération itérative différente à chaque tableau, puis stocké les résultats
-
Ensuite vérifié l’égalité des deux tableaux
Le script attend que @tab1.map( {is-prime($_ + 1)} )
finisse puis évalue @tab2.map( {is-prime($_ - 1)} )
Les opérations effectuées sur chaque tableau ne dépendent pas l’une de l’autre.
my @tab1 = (0..49999);
my @tab2 = (2..50001);
my $promesse1 = start @tab1.map( {is-prime($_ + 1)} ).eager;
my $promesse2 = start @tab2.map( {is-prime($_ - 1)} ).eager;
my @résultat1 = await $promesse1;
my @résultat2 = await $promesse2;
say @résultat1 eqv @résultat2;
say now - INIT now;
La méthode start
évalue le code et renvoie un objet de type promesse, ou plus brièvement une promesse (promise).
Si le code est évalué correctement, la promesse sera tenue (kept).
Si le code lance une exception, la promesse sera non tenue (broken).
La méthode await
s’attend à une promesse.
Si elle est tenue elle obtiendra les valeurs renvoyées.
Si elle n’est pas tenue, une exception sera lancée.
Vérifier le temps pris par chacun des scripts.
Le parallélisme ajoute toujours un surcoût lié à la gestion des processus en parallèle. Si cette surcharge n’est pas compensée par un gain dans le temps de calcul, le script paraîtra plus lent. C’est pourquoi utiliser race , hyper , start et await avec des scripts assez simples peut effectivement les ralentir.
|
14.2. Concurrence et Asynchronisme
Pour plus de détails sur la concurrence et la programmation asynchrone, voir https://docs.raku.org/language/concurrency |
15. Interface d’appel natif
Raku donne la possibilité d’utiliser de librairies C en utilisant l’Interface d’appel natif.
NativeCall
est un module standard inclus dans Raku qui facilite son interfacage avec le langage C.
15.1. Appeler une fonction
Considérez le code C ci-dessous qui définit la fonction hellofromc
.
Cette fonction affiche sur le terminal Bonjour depuis C
. Elle de prend pas d’argument et ne retourne aucune valeur.
#include <stdio.h>
void hellofromc () {
printf("Bonjour depuis\n");
}
Suivant votre système d’exploitation, lancez les commandes suivantes pour compiler ce code C dans une librairie.
gcc -c -fpic ncitest.c
gcc -shared -o libncitest.so ncitest.o
gcc -c ncitest.c
gcc -shared -o ncitest.dll ncitest.o
gcc -dynamiclib -o libncitest.dylib ncitest.c
Dans le même dossier où vous avez compilé votre librairie C, créez un nouveau fichier Raku qui contient le code suivant, et lancez-le.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hellofromc() is native(LIBPATH) { * }
hellofromc();
D’abord nous avons déclaré l’utilisation du module NativeCall
.
Puis nous avons créé une constante LIBPATH
qui contient le chemin vers la librairie C.
Notez que $*CWD
renvoie le dossier actuel de travail.
Puis nous avons créé une nouvelle routine Raku nomée hellofromc()
qui va agir comme envellope pour la fonction C du même nom qui se trouve dans LIBPATH
.
Cela est possible en utilisant le trait is native
.
Puis nous appelons notre routine Raku.
Ce qui revient simplement à déclarer une routine avec un trait is native
puis le nom de la librairie C.
15.2. Renommer une fonction
Dans la partie ci-dessus nous avons vu comment appler une simple fonction C en l’encepsulant dans une routine Raku qui a le même nom par l’utilisation du trait is native
.
Dans certains cas, nous pourions avoir besoin de changer le nom de la routine Raku.
Pour ce-faire, nous utilisons le trait is symbol
.
Modifions notre scrit Raku et renommons la routine hellofromc
en hello
.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hello() is native(LIBPATH) is symbol('hellofromc') { * }
hello();
Si la routine Raku a un nom différent de son équivalente en C, nous devons utiliser le trait is symbol
avec le nom de la fonction C originale.
15.3. Passer des arguments
Compilez la fonction C mofifiée et re-lancez le script ci-dessous.
Notez comment nous avons modifié le code C et Raku pour prendre en compte une chaîne (char*
en C et Str
avec Raku)
#include <stdio.h>
void hellofromc (char* name) {
printf("Bonjour, %s! Ceci est du C!\n", name);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hello(Str) is native(LIBPATH) is symbol('hellofromc') { * }
hello('Anne');
15.4. Retourner des valeurs
Reprenons le procédé encore une fois et créons une simple calculatrice qui additionne deux entiers.
Compilez la librairie C et lancez le script Raku.
int add (int a, int b) {
return (a + b);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub add(int32,int32) returns int32 is native(LIBPATH) { * }
say add(2,3);
Notez que la fonction Raku comme la C accepte deux entiers et en renvoient un (int
en C et int32
pour Raku)
15.5. Types
Vous avez pu vous interroger sur le fait que nous utilisons int32
au lieu de Int
dans le script Raku.
Certains Types comme Int
, Rat
etc. ne peuvnet pas être utilisés pour recevoir des valeurs depuis un fonction C.
On doit utiliser les même Types en Raku que ceux utilisés en C.
Heureusement Raku possède de nombreus types qui ont leurs equivalents en C.
Typage C | Typage Raku |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tableaux: Par exemple |
|
Pour plus d’informations sur l’interface d’appel natif, voir: https://docs.raku.org/language/nativecall |
16. La communauté
-
#raku IRC channel. De nombreuses discussions ont lieu sur IRC.
Votre première source d’infos si vous voulez dez réponses rapides: https://raku.org/community/irc -
StackOverflow, questions sur Raku pour avoir des explications plus détaillées à vos questions.
-
Rakudo Weekly Un résumé hebdomadaire des nouvelles concernant Raku.
-
pl6anet est un agrégateur de blog qui se concentre sur Raku.
-
/r/rakulang pour vous inscrire subreddit du langage.
-
@perl6org la communauté sur Twitter.