Dit document is bedoeld om je een kort overzicht te geven van de Raku programmeertaal. Het is zo opgezet dat je snel leert iets met Raku te kunnen doen.
Sommige onderdelen van dit document verwijzen naar (completere en preciezere) delen van de (Engelstalige) Raku documentatie. Als je meer informatie over een bepaald onderwerp nodig hebt, is dat de beste plaats om die te vinden.
Je zult voorbeelden vinden bij de meeste onderwerpen in dit document. Neem de tijd om ze allemaal uit te proberen om ze beter te begrijpen.
De inhoud is gelicenseerd met de Creative Commons Attribution-ShareAlike 4.0 International License.
Om deze in te zien, ga naar
Als je wilt bijdragen aan dit document, ga dan naar:
Alle commentaar is welkom:
-
naoum@hankache.com - Engels
-
rakuguide@liz.nl - Nederlands
1. Inleiding
1.1. Wat is Raku
Raku is een hogere programmeertaal voor algemeen gebruik met graduele typering. Men kan in Raku kan met diverse paradigma’s programmeren. Ondersteund worden onder andere procedureel, object-georiënteerd en functioneel programmeren.
-
TIMTOWTDI (uitgesproken "Timtoodi", oftewel "TimToady" in het Engels): There Is More Than One Way To Do It. (Er is meer dan één manier om het te doen)
1.2. Jargon
-
Raku: Is een taalspecificatie met een verzameling tests. Een implementatie van Raku die al deze tests succesvol kan uitvoeren, mag zich een implementatie "Raku" noemen.
-
Rakudo: Is een compiler voor Raku.
-
Zef: Is een installatie-programma voor Raku modules.
-
Rakubrew: Is een installatie-programma voor Raku en een versie-manager.
1.3. Installeren van Raku
Er zijn een aantal manieren om Raku te installeren.
1.3.1. Rakubrew
De aanbevolen manier om Raku te installeren is door middel van Rakubrew, een installatie-programma voor Rak en tevens versie-manager.
Als eerste moet men Rakubrew installeren door een van de volgende commando’s uit te voeren, afhankelijk van het operating system dat men gebruikt.
curl https://rakubrew.org/install-on-perl.sh | sh
curl https://rakubrew.org/install-on-macos.sh | sh
. {iwr -useb https://rakubrew.org/install-on-powershell.ps1 } | iex
Het programma eindigt met het tonen van een commando dat Rakubrew beschikbaar
maakt zodra een terminalvenster wordt geopend.
Kopieer dat commando, voer het uit en sluit dan dat terminalvenster. En open dan een nieuw terminal venster.
Installeer dan de meest recente versie van Raku met:
rakubrew download
Om er zeker van te zijn dat de installatie is gelukt, voer dan dit commando uit:
raku --version
1.3.2. Installeerbare Componenten
De Rakudo compiler maakt ook installeerbare componenten beschikbaar voor
Linux, MacOS en Windows.
Deze kunnen worden opgehaald worden van https://rakudo.org/downloads
Haal het installeer programma op (bestand met de .msi extensie) en pak het uit. Het installeer programma voegt Raku en Zef to aan het PATH.
Haal het archief-bestand op (met de .tar.gz extensie) en pak het uit. De programma’s bevinden zich in de bin en share/perl6/site/bin mappen van het archief. Voeg deze mappen toe aan je PATH.
1.3.3. Rakudo Star
Rakudo Star is een bundel waarin zich Rakudo, Zef, een collectie van Raku modules en documentatie bevinden. Het kan worden geïnstalleerd met een van de volgende manieren:
-
Docker:
docker pull rakudo-star -
Scoop op Windows:
scoop install rakudo-star -
Homebrew op MacOS:
brew install rakudo-star
| Voor andere mogelijkheden, ga naar https://raku.org/install |
1.4. Uitvoeren van Raku code
Je kunt eenvoudig Raku code uitvoeren in de REPL (Read-Eval-Print-Loop, oftewel een lees, evalueer, print, lus).
Open daarvoor een terminalvenster, type raku en druk op [Enter]. Er verschijnt dan een > prompt.
Vervolgens kun je een regel code intypen en weer op [Enter] drukken. De REPL zal dan de uiteindelijke waarde van die code laten zien op het scherm. Je kunt dan weer een regel code intypen, of exit intypen en op [Enter] drukken om de REPL te verlaten.
Je kunt je code natuurlijk ook opslaan in een bestand, dat je daarna gaat uitvoeren.
We raden aan om een Raku script de extensie .raku te geven, zodat het later te herkennen is als Raku bestand.
Voer het bestand uit door raku bestandsnaam.raku in het terminalvenster in te typen en op [Enter] te drukken. Anders dan bij de REPL zal die niet automatisch het resultaat van elke regel laten zien: daarvoor moet je een opdracht als say in je programma plaatsen om iets te tonen.
De REPL wordt meestal gebruikt om een specifiek stukje code uit te proberen, meestal niet meer dan één enkele regel. Voor programma’s die uit meer dan één regel bestaan, wordt het aangeraden om die regels in een bestand op te slaan en dan dat bestand uit te voeren.
Je kunt ook een regel code non-interactief uitproberen op de commando-regel in een terminalvenster, door raku -e 'jouw regel code' in te typen en dan op [Enter] te drukken.
|
Een regel-editor helpt je om alles uit een REPL te krijgen. Het maakt het mogelijk om de cursor-toetsen te gebruiken om vorige/volgende regels te herhalen, en de invoer aan te passen, waarbij TAB gebruikt kan worden voor woorden te completeren. Indien dit nog niet werkt, dan kun je dit command uitvoeren:
|
1.5. Bewerkingsprogramma’s (Editors)
Aangezien je het grootste deel van je tijd Raku programma’s in bestanden aan het opslaan bent, is het handig om een goede editor te hebben die Raku syntax herkent.
-
Visual Studio Code: syntax-markering en fout-meldingen met raku-navigator
-
IntelliJ IDEA: Raku ondersteuning mogelijk met raku-intellij-plugin
-
Nano: syntax-markering met raku.nanorc
-
Notepad++: syntax-markering standaard meegeleverd
-
Kate: syntax-markering standaard meegleverd
-
Geany: syntax-markering standaard meegleverd
1.6. Hello World!
Laten we beginnen met het hello world ritueel.
say 'hello world';
hetgeen ook geschreven kan worden als:
'hello world'.say;
1.7. Syntax overview
Raku kent weinig beperkingen: over het algemeen kun je zoveel spaties (witruimte) gebruiken als je zelf wilt. In een aantal gevallen is de witruimte wel van belang.
Opdrachten bestaan over het algemeen uit een regel code die beëindigd wordt door een punt-komma:
say "Hallo" if True;
say "Wereld" if False;
Punt-komma’s zijn niet nodig na de laatste opdracht in een bestand of een blok programma-code, maar het is een goed gebruik om ze dan in dat geval toch toe te voegen.
Een Blok kan bestaan uit nul of meer opdrachten. Je maakt een blok door accolades om de opdrachten heen te plaatsen.
{
say "Eerste opdracht in het blok.";
say "Tweede opdracht in het blok.";
}
Expressies zijn een speciaal soort opdracht die resulteren in een waarde:
1+2 geeft 3 terug als waarde
Expressies bestaan uit Termen en Operatoren.
Termen zijn:
-
Variabelen: Een waarde die bekeken en veranderd kan worden.
-
Literals (Letterlijke waarden): een constante waarde zoals een getal of een aantal letters (string).
Operatoren worden onderverdeeld in deze typen:
Type |
Uitleg |
Voorbeeld |
Prefix |
Voor een term |
|
Infix |
Tussen twee termen |
|
Postfix |
Volgt na een term |
|
Circumfix |
Staat om een term heen |
|
Postcircumfix |
Achter een term, om een andere term heen |
|
1.7.1. Naamgeving
Je moet termen een naam geven op het moment dat je ze definieert.
-
Ze moeten beginnen met een alphabetisch karakter of een liggend streepje (underscore).
-
Ze mogen cijfers bevatten (behalve als eerste karakter).
-
Ze mogen een of meer koppeltekens
-en/of enkele aanhalingstekens'bevatten (mits omgeven door alphabetische karakters, dus niet als eerste of laatste karakter).
Geldig |
Niet geldig |
|
|
|
|
|
|
|
|
|
|
-
Kameelkast (Camel case):
variableNo1 -
Kebabkast (Kebab case):
variable-no1 -
Slangenkast (Snake case):
variable_no1
Je mag je termen namen geven zoals je zelf wilt, maar het is een goede gewoonte om vast te houden aan een enkele naamgevingsconventie in een programma.
Het gebruik van betekenisvolle namen zal jouw leven als programmeur gemakkelijker maken (en van anderen die later aan jouw programma moeten werken).
-
var1 = var2 * var3is syntactisch correct, maar de betekenis is niet duidelijk. -
maandsalaris = dagloon * gewerkte-dagengeeft beter aan waar het hierover gaat.
1.7.2. Commentaar
Een commentaar is een stuk tekst dat bij uitvoering genegeerd wordt, maar van belang kan zijn voor de lezer van de programma-code.
Er zijn 3 manieren om commentaren in een programma te stoppen:
-
Enkele regel:
# Dit is een regel met commentaar -
Als onderdeel van een regel (embedded):
say #`(Dit is een ingebed commentaar) "Hallo wereld." -
Meer dan één regel
=begin comment Dit is een commentaar over meer dan één regel Commentaar 1 Commentaar 2 =end comment
1.7.3. Aanhalingstekens (Quotes)
Een string wordt gedefinieerd door middel van enkele of dubbele aanhalingstekens.
Gebruik altijd dubbele aanhalingstekens:
-
als er een enkel aanhalingsteken in de string voorkomt.
-
als de string een variabele bevat die geïnterpoleerd moet worden.
say 'Hallo Wereld'; # Hallo Wereld
say "Hallo Wereld"; # Hallo Wereld
say "Doe 't niet"; # Doe 't niet
my $naam = 'Jan Jansen';
say 'Hallo $naam'; # Hallo $naam
say "Hallo $naam"; # Hallo Jan Jansen
2. Operatoren
Operatoren zijn de symbolen en woorden die waarden combineren in expressies, bijvoorbeeld + in 1 + 2
2.1. Algemene operatoren
Onderstaande tabel toont de meest voorkomende operatoren.
2.1.1. Rekenkundig
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
Optelling |
|
|
|
|
Aftrekking |
|
|
|
|
Vermenigvuldiging |
|
|
|
|
Machtsverheffen |
|
|
|
|
Delen |
|
|
|
|
Geheel getal deling (rond af) |
|
|
|
|
Modulo |
|
|
|
|
Deelbaarheid |
|
|
|
|
|||
|
|
Grootse gemene deler |
|
|
|
|
Kleinste gemene veelvoud |
|
|
|
|
Numeriek gelijk |
|
|
|
|
Numeriek niet gelijk |
|
|
|
|
Numeriek kleiner dan |
|
|
|
|
Numeriek groter dan |
|
|
|
|
Numeriek kleiner dan of gelijk aan |
|
|
|
|
Numeriek groter dan of gelijk aan |
|
|
|
|
Numeriek meer/minder/gelijk |
|
|
|
|
|||
|
|
2.1.2. Vergelijken
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
String gelijk |
|
|
|
|
String niet gelijk |
|
|
|
|
String kleiner dan |
|
|
|
|
String groter dan |
|
|
|
|
String kleiner dan of gelijk |
|
|
|
|
String groter dan of gelijk |
|
|
|
|
String meer/minder/gelijk |
|
|
|
|
|||
|
|
|||
|
|
Slimme meer/minder/gelijk |
|
|
|
|
|||
|
|
Slim vergelijken |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
2.1.3. Toewijzing en strings
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
Toewijzing |
|
|
|
|
Strings aaneenschakelen |
|
|
|
|
|||
|
|
String herhalen |
|
|
|
|
2.1.4. Verhogen en verlagen
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
Verhoging |
|
|
|
Verhoging |
|
|
|
|
|
Verlaging |
|
|
|
Verlaging |
|
|
2.1.5. Conversie
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
Converteer naar de numerieke waarde |
|
|
|
|
|||
|
|
|||
|
|
Converteer naar de negatieve numerieke waarde |
|
|
|
|
|||
|
|
|||
|
|
Converteer naar de logische waarde |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Converteer naar het tegenovergestelde van de logische waarde |
|
|
2.1.6. Reeksen en lijsten
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
Reeks constructeur |
|
|
|
|
Reeks constructeur |
|
|
|
|
Reeks constructeur |
|
|
|
|
Reeks constructeur |
|
|
|
|
Reeks constructeur |
|
|
|
|
Luie reeks constructeur |
|
|
|
|
Pletten |
|
|
|
|
2.2. Logische operatoren
Logische operatoren combineren condities of kunnen kiezen tussen 2 waarden aan de hand van een conditie.
| Operator | Type | Beschrijving | Voorbeeld | Resultaat |
|---|---|---|---|---|
|
|
True indien beide waarden waar zijn |
|
|
|
|
|||
|
|
True indien minstens één van de waarden waar is |
|
|
|
|
|||
|
|
Zelfde als |
|
|
|
|
Zelfde als |
|
|
|
|
Gedefinieerd-of: linker waarde indien gedefinieerd, anders de rechter waarde |
|
|
|
|
|||
|
|
Selecteer eerste waard indien conditie waar, anders de tweede waarde |
|
|
|
|
Logische operatoren worden meestal gebruikt om meerdere vergelijkingen te combineren.
my $age = 30;
say $age >= 18 && $age <= 65; # True
say $age < 18 || $age > 65; # False
De gedefinieerd-of operator // is een gemakkelijke manier of verstek waarde aan te geven.
my $name;
say $name // 'Anoniem'; # Anoniem
2.3. Omkeringsoperatoren
Door een R te plaatsen direct voor een operator, zorg je ervoor dat de termen omgewisseld worden.
| Normale operator | Resultaat | Omkeringsoperator | Resultaat |
|---|---|---|---|
|
|
|
|
|
|
|
|
2.4. Herleidingsoperatoren
Herleidingsoperatoren werken op lijsten.
Je maakt een herleidingsoperator door vierkante haken om de operator te plaatsen []
| Normal Operator | Resultaat | Herleidingsoperator | Resultaat |
|---|---|---|---|
|
|
|
|
|
|
|
|
| Voor een compleet overzicht van operatoren, inclusief hun prioriteit, ga dan naar https://docs.raku.org/language/operators |
3. Variabelen
Raku variabelen kunnen worden geclassificeerd in 3 categorieën: Scalars, Arrays en Hashes.
Een sigil is een karakter dat als prefix gebruikt wordt om aan te geven in welke categorie een variabele hoort.
-
$geeft een scalar aan -
@geeft een array aan -
%geeft een hash aan
Een variabele moet worden gedefinieerd voordat het gebruikt kan worden.
Dit kan men doen door het woord my vóór de naam van de variabele te plaatsen.
| Deze inleiding geeft een versimpeld beeld van variabelen, geschikt om de basis elementen van Raku te leren. Om meer te begrijpen over variabele, zie https://docs.raku.org/language/containers |
3.1. Scalars
Een scalar kan één waarde bevatten.
#String
my $naam = 'Jan Jansen';
say $naam;
#Integer
my $leeftijd = 99;
say $leeftijd;
Afhankelijk van het type waarde dat een scalar bevat, kun je daar bepaalde operaties op uitvoeren.
3.1.1. String
my $naam = 'Jan Jansen';
say $naam.uc;
say $naam.chars;
say $naam.flip;
UitvoerJAN JANSEN 10 nesnaJ naJ
| Bekijk https://docs.raku.org/type/Str voor de complete lijst van methoden die men op een string kan uitvoeren. |
3.1.2. Integer
my $leeftijd = 17;
say $leeftijd.is-prime;
UitvoerTrue
| Bekijk https://docs.raku.org/type/Int voor de complete lijst van methoden die men op een geheel getal (integer) kan uitvoeren. |
3.1.3. Rationeel getal
my $leeftijd = 2.3;
say $leeftijd.numerator;
say $leeftijd.denominator;
say $leeftijd.nude;
Uitvoer23 10 (23 10)
| Bekijk https://docs.raku.org/type/Rat voor de complete lijst van methoden die men op een rationeel getal kan uitvoeren. |
3.2. Arrays
Arrays bestaan uit een lijst van scalar variabelen.
my @dieren = 'kameel', 'lama', 'uil';
say @dieren;
Vele operaties kunnen op arrays uitgevoerd worden, zoals getoond in onderstaand voorbeeld:
De tilde ~ wordt gebruikt om strings aan elkaar te plakken.
|
Scriptmy @dieren = 'kameel','vicuña','lama';
say "De dierentuin heeft " ~ @dieren.elems ~ " dieren";
say "De dieren zijn: " ~ @dieren;
say "Ik ga een uil adopteren voor de dierentuin";
@dieren.push("owl");
say "Nu heeft mijn dierentuin: " ~ @dieren;
say "Het eerste dier dat we adopteerden was de " ~ @dieren[0];
@dieren.pop;
say "Helaas is de uil ontsnapt, dus hebben we nu alleen nog: " ~ @dieren;
say "We gaan de dierention sluiten en houden nog maar één dier over";
say "We laten de " ~ @dieren.splice(1,2) ~ " gaan en houden de " ~ @dieren;
UitvoerDe dierentuin heeft 3 dieren De dieren zijn: kameel vicuña lama Ik ga een uil adopteren voor de dierentuin Nu heeft mijn dierentuin: kameel vicuña lama uil Het eerste dier dat we adopteerden was de kameel Helaas is de uil ontsnapt, dus hebben we nu alleen nog: kameel vicuña lama We gaan de dierention sluiten en houden nog maar één dier over We laten de vicuña llama gaan en houden de kameel
.elems geeft het aantal elementen in een array.
.push() voegt een element toe aan een array.
We kunnen een specifiek element van een array bekijken door de positie aan te geven @dieren[0].
.pop verwijdert het laatste element van het array.
.splice(a,b) verwijdert b elementen vanaf positie a.
Hieronder vind je nog een paar voorbeelden van operaties die op een array kunnen worden uitgevoerd:
Scriptmy @dieren = 'kameel', 'vicuña', 'lama';
say "Het laatste dier is een " ~ @dieren[*-1];
say "De eerste twee dieren zijn: " ~ @dieren[0, 1];
say "Allo dieren: " ~ @dieren.join(', ');
@dieren.unshift("uil");
say "Een uil kwam er als eerste bij: " ~ @dieren;
@dieren.shift;
say "De uil is alweer gevlogen: " ~ @dieren;
UitvoerHet laatste dier is een lama De eerste twee dieren zijn: kameel vicuña Alle dieren: kameel, vicuña, lama Een uil kwam er als eerste bij: uil kameel vicuña lama De uil is alweer gevlogen: kameel vicuña lama
@dieren[*-1] geeft het laatste element van een array.
@dieren[0, 1] geeft meerdere elementen in één keer. Dit heet een plak (slice).
.join(', ') geeft een enkele string gemaakt door alle elementen, gescheiden door een komma en een spatie, samen te voegen.
.unshift() voegt één of meer elementen toe aan het begin van een array.
.shift verwijdert het eerste element van een array en geeft dat terug.
3.2.1. Veranderingen in een array zelf
Sommige methoden veranderen de array waar ze op worden aangeroepen. Anderd laten de array onveranderd en geven een waarde terug.
Het is belangrijk om te weten welke method wat doet.
Scriptmy @nummers = 7, 2, 4, 9, 11, 3;
@nummers.push(99);
say @nummers; #1
say @nummers.sort; #2
say @nummers; #3
@nummers .= sort;
say @nummers; #4
Uitvoer[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 verandert een array. Een methode die dat doet, noemen we een mutator (#1)
.sort laat een array onveranderd. Geeft de waarden van de array gesorteerd terug:
-
(#2) laat de gesorteerde waarden zien die terug zijn gegeven.
-
(#3) laat zien dat de array niet is veranderd.
Om het resultaat van een zo’n methode direct in het array terug te plaatsen gebruiken we .= in plaats van . (#4)
@nummers .= sort is een kortere manier om @nummers = @nummers.sort te schrijven
Het resultaat in #2 werd getoond met (ronde) haken in plaats van vierkante haken omdat .sort een lijst teruggeft in plaats van een array.
Nadat die waarden weer in @nummers is geplaatst, is het weer een array geworden.
|
.= werkt met alle methodes en op alle variabelen. Bijvoorbeeld, $naam .= uc vervangt de waarde van $naam met de versie in hoofdletters.
|
3.2.2. Arrays met beperkt aantal elementen
Een gewoon array kun je als volgt specificeren:
my @array;
Een gewoon array is niet beperkt wat betreft aantal elementen, het past zichzelf aan (auto-extending).
Men kan in een gewoon array zoveel waarden opslaan als men wil.
Daarentegen is het ook mogelijk om een array aan te maken met een beperkt aantal elementen. Dit soort arrays verbieden toegang tot niet-bestaande elementen.
Specificeer het aantal elementen in vierkante haken direct achter de naam van een array om een array met beperkt aantal elementen te specificeren:
my @array[3];
Dit array kan hoogstens 3 waarden bevatten, met als indexwaarden 0 t/m 2.
my @array[3];
@array[0] = "eerste waarde";
@array[1] = "tweede waarde";
@array[2] = "derde waarde";
Het is niet mogelijk om een vierde waarde aan dit array toe te voegen:
my @array[3];
@array[0] = "eerste waarde";
@array[1] = "tweede waarde";
@array[2] = "derde waarde";
@array[3] = "vierde waarde";
Index 3 for dimension 1 out of range (must be 0..2)
3.2.3. Multidimensionele arrays
De arrays die we tot nu toe gezien hebben, hadden maar één dimensie.
We kunnen echter ook arrays met meer dan één dimensie in Raku specificeren.
my @tbl[3;2];
Dit array heeft 2 dimensies. De eerste dimensie kan maximaal 3 waarden hebben, en de tweede dimensie maximaal 2 waarden.
Zie het als een rooster van 3x2.
my @tbl[3;2];
@tbl[0;0] = 1;
@tbl[0;1] = "x";
@tbl[1;0] = 2;
@tbl[1;1] = "y";
@tbl[2;0] = 3;
@tbl[2;1] = "z";
say @tbl
Uitvoer[[1 x] [2 y] [3 z]]
[1 x] [2 y] [3 z]
| Zie https://docs.raku.org/type/Array voor volledige informatie over arrays. |
3.3. Hashes
3.3.1. Het maken van een hash
my %hoofdsteden = 'VK','Londen','Duitsland','Berlijn';
say %hoofdsteden;
Uitvoer{Duitsland => Berlijn, VK => Londen}
my %hoofdsteden = VK => 'Londen', Duitsland => 'Berlijn';
say %hoofdsteden;
Uitvoer{Duitsland => Berlijn, VK => Londen}
Dit zijn een aantal van de methoden die men op een hash kan uitvoeren:
Scriptmy %hoofdsteden = VK => 'Londen', Duitsland => 'Berlijn';
%hoofdsteden.push: (Frankrijk => 'Parijs');
say %hoofdsteden.kv;
say %hoofdsteden.keys;
say %hoofdsteden.values;
say "De hoofdstad van Frankrijk is: " ~ %hoofdsteden<Frankrijk>;
Uitvoer(Frankrijk Parijs Duitsland Berlijn VK Londen) (Frankrijk Duitsland VK) (Parijs Berlijn Londen) De hoofdstad van Frankrijk is: Parijs
.push: (naam => 'Waarde') voegt een nieuwe naam/waarde paar toe.
.kv geeft een lijst met alle namen en waarden terug.
.keys geeft een lijst met alle namen terug.
.values geeft een lijst met alle waarden terug.
De waarde behorende bij een gegeven naam kun je opvragen door die naam te specificeren %hash<naam>
| Raku houdt geen bepaalde volgorde van de namen in een hash aan. The volgorde waarin de landen in de bovenstaande uitvoer kan anders zijn, en ook verschillen met elke keer dat je die code uitvoert. |
3.3.2. Controleren en verwijderen van een naam
Men kan ook controleren of een naam bestaat in een hash, en de naam eruit verwijderen.
Scriptmy %hoofdsteden = VK => 'Londen', Duitsland => 'Berlijn';
say %hoofdsteden<Duitsland>:exists;
say %hoofdsteden<Frankrijk>:exists;
%hoofdsteden<Duitsland>:delete;
say %hoofdsteden;
UitvoerTrue
False
{VK => Londen}
%hash<key>:exists geeft True indien de naam bestaat in de hash.
%hash<key>:delete verwijdert de naam uit de hash.
| Zie https://docs.raku.org/type/Hash voor alle informatie over hashes. |
3.4. Types
3.4.1. Dynamische en statische typeringen
In de voorafgaande voorbeelden hebben we niet het type van de waarde aangegeven die in een variabele opgeslagen kan worden.
.WHAT geeft het type van de waarde in een variabele terug.
|
my $var = 'Tekst';
say $var;
say $var.WHAT;
$var = 123;
say $var;
say $var.WHAT;
Zoals je kunt zien in bovenstaand voorbeeld, was het type van de waarde in $var eerst (Str) en daarna (Int).
Deze stijl van programmeren wordt dynamische typering (dynamic typing) genoemd. Dynamisch in de betekenis dat de variable waarden mag bevatten van elk (Any) type.
Probeer nu onderstaand voorbeeld uit te voeren:
Merk op dat we Int voor de naam van de variabele hebben geplaatst.
my Int $var = 'Tekst';
say $var;
say $var.WHAT;
Dit zal fout gaan en terug komen met het foutbericht: Type check failed in assignment to $var; expected Int but got Str
Wat hier gebeurde is dat we van te voren hadden aangegeven dat de variabele alleen maar (Int) mag accepteren. Toen we probeerden om er een string (Str) aan toe te wijzen, was dat niet mogelijk en ging het fout.
Deze stijl van programmeren wordt "statische typering" (static typing) genoemd. Statisch omdat het type van variabelen wordt gedefinieerd voordat er aan wordt toegewezen, en deze later niet kan worden veranderd.
Raku wordt aangeduid met "graduele typering": het laat namelijk zowel statische als dynamische typering toe.
3.4.2. Getypeerde arrays en hashes
my Int @array = 1,2,3;
say @array;
say @array.WHAT;
my Str @veeltalig = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは";
say @veeltalig;
say @veeltalig.WHAT;
my Str %hoofdsteden = (VK => 'Londen', Duitsland => 'Berlijn');
say %hoofdsteden;
say %hoofdsteden.WHAT;
my Int %landennummers = (VK => 44, Duitsland => 49);
say %landennummers;
say %landennummers.WHAT;
3.4.3. Veel voorkomende types
Hieronder vind je een lijst van meest voorkomende typen:
Je zult hoogstwaarschijnlijk de eerste twee nooit gebruiken, maar we laten ze hier zien om je te laten weten dat ze bestaan.
|
|
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.5. Introspectie
Met introspectie bedoelen we het process waarmee we informatie over de eigenschappen van een object kunnen bekijken, zoals het type.
In een van de vorige voorbeelden gebruikten we .WHAT om het type van een variabele te achterhalen.
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)
Het type van een variabele waarin een waarde is opgeslagen, is gecorreleerd aan die waarde.
Het type van een lege variabele die gespecificeerd is met een type, is het type waarmee het werd gespecificeerd.
Het type van een lege variabele die niet is gespecificeerd met een type, is (Any)
Om de waarde uit een variabele te verwijderen, kun je de waarde Nil toewijzen.
3.6. Bereik (scoping)
Voordat men een variabele voor de eerste keer kan gebruiken, moet deze worden gedefinieerd.
3.6.1. Statisch bereik
Dit kan op diverse manieren in Raku, my is wat we tot nu toe in de bovenstaande voorbeelden hebben gebruikt.
my $var = 1;
Met my geeft men de variabele een statisch bereik (ook wel lexicaal bereik genoemd).
In andere woorden, de variabele zal alleen maar toegankelijk zijn in het gebied (scope) waarin het was gedefinieerd.
Zo’n gebied (scope) wordt in Raku begrensd door { }.
Een variabele is toegankelijk in het gehele Raku script als er geen gebiedsbegrenzing is.
{
my Str $var = 'Tekst';
say $var; # is toegankelijk
}
say $var; #is niet toegankelijk, geeft een foutmelding
Aangezien zo’n variabele alleen toegankelijk is in het gebied waarin het was gedefinieerd, kan men dezelfde naam voor een variabele gebruiken in een ander gebied.
{
my Str $var = 'Tekst';
say $var;
}
my Int $var = 123;
say $var;
3.6.2. Andere bereiken
Andere manieren om een bereik / toestand van een variabele aan te geven.
-
ourgeeft een variabele aan in het bereik van een package. Het is zichtbaar buiten het bereik waarin het wordt gedefinieerd. -
constantgeeft een "variabele" aan die niet kan worden veranderd.
constant $tau = pi * 2;
$tau = 6; # geeft foutmelding: Cannot assign to an immutable value
3.7. Toewijzing vs. verbinden
3.7.1. Toewijzing
We hebben in de vorige voorbeelden gezien hoe we waarden aan variabelen kunnen toewijzen.
Toewijzing wordt gedaan met de = operator.
my Int $var = 123;
say $var;
We kunnen de waarde van een variabele veranderen:
Scriptmy Int $var = 123;
say $var;
$var = 999;
say $var;
Uitvoer123 999
3.7.2. Verbinden
Daarentegen kunnen we de waarde van een variabele niet veranderen als deze is verbonden met een waarde.
Verbinding wordt gedaan met de := operator.
Scriptmy Int $var := 123;
say $var;
$var = 999;
say $var;
Uitvoer123 Cannot assign to an immutable value
3.7.3. Verbinden met variabelen
Variabelen kunnen ook verbonden worden met andere variabelen:
Scriptmy $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Uitvoer7 8
Het verbinden van variabelen werkt twee kanten op, zoals je al gezien hebt.
$a := $b en $b := $a hebben hetzelfde effect.
| Zie https://docs.raku.org/language/variables voor meer informatie over variabelen. |
4. Strings
We hebben strings al vele malen gebruikt in vorige voorbeelden.
In dit hoofdstuk gaan we wat dieper in op wat er allemaal met strings kan worden gedaan.
4.1. Interpolatie
Een string gedefineerd met dubbele aanhalingstekens vervangt de variabelen met hun waarde.
Dit wordt "interpolatie" genoemd.
my $naam = 'Jan';
my $leeftijd = 23;
say "$naam is $leeftijd jaar oud";
Interpolatie is niet beperkt tot variabelen. Elk stuk programma code dat omgeven wordt door accolades zal worden uitgevoerd, en het resultaat daarvan wordt in de string geplaatst.
Scriptmy $naam = 'Jan';
my $leeftijd = 23;
say "$naam wordt volgend jaar {$leeftijd + 1}";
say "De naam $naam heeft {$naam.chars} letters";
say 'De naam $naam heeft {$naam.chars} letters";
UitvoerJan wordt volgend jaar 24.
De naam Jan heeft 3 letters.
'De naam $naam heeft {$naam.chars} letters";
De laatste string werd gemaakt met enkele aanhalingstekens, waarin niets wordt geïnterpoleerd.
4.2. Word lists
A list of words can be written without quotes and without commas, by enclosing it in angle brackets.
my @dieren = <kameel lama uil>;
say @dieren;
Is een kortere manier van schrijven dan:
my @dieren = 'kameel', 'lama', 'uil';
say @animals;
| De woorden worden gescheiden door spaties. Deze notatie kan niet worden gebruikt voor waarden waarin zich een spatie bevindt. |
4.3. Veel gebruikte methoden
Op een string kunnen vele methoden worden aangeroepen, zoals hieronder is te zien:
Scriptmy $tekst = 'Raku is lol';
say $tekst.chars;
say $tekst.uc;
say $tekst.lc;
say $tekst.flip;
say $tekst.contains('lol');
say $tekst.starts-with('Raku');
say $tekst.ends-with('boring');
say $tekst.substr(0, 4);
say $tekst.index('is');
say ' Raku '.trim;
Uitvoer11 RAKU IS LOL raku is lol lol si ukaR True True False Raku 5 Raku
.chars geeft het aantal karakters in de string.
.uc en .lc geeft de string in hoofdletters of kleine letters.
.flip geeft de string met de karakters achterstevoren.
.contains() geeft aan of een string in de string voorkomt.
.starts-with() en .ends-with() geeft aan of een string begint of eindigt met een andere string.
.substr(a, b) geefts b karakters beginnend op positie a.
.index() geeft de positie van een string in een andere string.
.trim geeft een string zonder de witruimte aan begin en einde van de string
Geen van deze methoden verandert de originele string. Gebruik .= om het resultaat in de variabele terug te plaatsen, zoals we al eerder deden met arrays.
|
4.4. Splijten en samenvoegen
Een string kan worden gespleten in een lijst, en een lijst kan worden samengevoegd tot een string.
Scriptmy $zin = "Pa's wijze lynx bezag";
my @woorden = $zin.woorden;
say @woorden;
say @woorden.elems;
say @woorden.join('-');
my $record = 'Jan,23,Nederlander';
my @velden = $record.split(',');
say @velden;
say @velden[0];
say 'Raku'.comb;
Uitvoer[Pa's wijze lynx bezag] 4 Pa's-wijze-lynx-bezag [Jan 23 Nederlander] Jan (R a k u)
.words splijt een string in woorden.
.join() combineert de gegeven lijst in een string met de gegeven separator tussen de elementen.
.split() splijt een string op de gegeven separator.
.comb geeft een lijst met elke letter van de string apart..
4.5. Formatteren
sprintf bouwt een string naar aanleiding van het gegeven sjabloon.
Het sjabloon bevat formatteer aanduidingen die beginnen met %. Elke aanduiding wordt vervangen door een van de waarden die worden meegegeven.
Scriptsay sprintf('%s is %d jaar oud', 'Jan', 23);
say sprintf('De prijs is %.2f euro', 3.14159);
say sprintf('Kaart nummer %05d', 42);
UitvoerJan is 23 jaar oud De prijs is 3.14 euro Kaart nummer 00042
%s geeft een string aan.
%d geeft een geheel getal (integer) aan.
%.2f geeft een getal afgerond op 2 decimalen aan.
%05d geeft een geheel getal, uitgevuld tot 5 posities.
| Voor de complete lijst van methoden die op strings kunnen worden uitgevoerd, zie https://docs.raku.org/type/Str |
5. Lussen en condities
Raku heeft een veelheid aan conditionele- en lusconstructies.
5.1. if
De code in het bereik van de conditionele constructie wordt alleen maar uitgevoerd als de conditie waar (True) is.
my $leeftijd = 19;
if $leeftijd > 18 {
say 'Welkom'
}
In Raku kunnen we de volgorde van de code en de conditie omkeren.
Maar zelfs als de volgorde is omgekeerd, zal de conditie altijd eerst worden uitgevoerd.
my $leeftijd = 19;
say 'Welkom' if $leeftijd > 18;
We kunnen alternatieve bereiken voor uitvoering aangeven voor het geval dat de conditie niet waar is:
-
else -
elsif
#voer deze code uit voor verschillende waarden van de variabele
my $aantal-stoelen = 9;
if $aantal-stoelen <= 5 {
say 'Ik ben een personenauto'
} elsif $aantal-stoelen <= 7 {
say 'Ik ben een busje'
} else {
say 'Ik ben een bus'
}
5.2. unless
De tegenovergestelde, ontkennende versie van een if command is unless (tenzij).
Deze code:
my $schone-schoenen = False;
if not $schone-schoenen {
say 'Maak je schoenen schoon'
}
Kan geschreven worden als:
my $schone-schoenen = False;
unless $schone-schoenen {
say 'Maak je schoenen schoon'
}
Ontkenning (negation) wordt in Raku gedaan met ! of not.
unless (conditie) kan worden gebruikt in plaats van if not (conditie).
unless kan geen else bereik hebben.
5.3. with
with gedraagt zich als een if commando, maar kijkt of de variabele een waarde heeft.
my Int $var=1;
with $var {
say 'Hallo'
}
Als je deze code uitvoert zonder dat je een waarde aan de variabele hebt toegekend, dan zou je geen uitvoer moeten zien.
my Int $var;
with $var {
say 'Hallo'
}
without is de ontkennende versie van with. Net als unless van if.
Als de eerste with niet waar is, dan kan men een alternatief bereik aangeven met orwith.
Je kunt with en orwith zien als een soort if en elsif.
5.4. for
Met het for commando kun je over een aantal waarden repeteren.
5.4.1. Arrays
my @array = 1,2,3;
for @array -> $array-item {
say $array-item * 100
}
Merk op dat we een lusvariabele $array-item aanmaken om de operatie *100 op elk element van het array uit te kunnen voeren.
Indien er geen lusvariabele wordt gegeven, dan wordt elke waarde in de variabele $_ geplaatst, ook wel bekend als "de verstek-variabele".
my @array = 1, 2, 3;
for @array {
say $_ * 100;
}
5.4.2. Reeksen
De waarden kunnen ook van een reeks afkomen:
for 1..5 -> $nummer {
say $nummer;
}
Men kan ook een methode aanroepen op de verstek-variable door alles voor de punt weg te laten.
Met andere woorden, .say is een kortere schrijfwijze voor $_.say
for 1..5 {
.say;
}
5.4.3. Hashes
Om alle waarden van een hash te bekijken, kan men met de kv methode vragen om de namen en de waarden in the hash, en daar twee lusvariabelen voor aanmaken:
my %hoofdsteden = VK => 'Londen', Duitsland => 'Berlijn';
for %hoofdsteden.kv -> $land, $stad {
say "De hoofdstad van $lang is $stad";
}
5.5. given
given is het Raku equivalent van het switch commando in andere programmeertalen, maar het is veel krachtiger.
my $var = 42;
given $var {
when 0..50 { say 'Minder dan of gelijk aan 50'}
when Int { say "is een Int" }
when 42 { say 42 }
default { say "huh?" }
}
Het testen van condities stops zodra een conditie van een when waar is geweest.
Met proceed kun je in Raku aangeven dat je door wilt gaan met testen van condities nadat een conditie waar was.
my $var = 42;
given $var {
when 0..50 { say 'Minder dan of gelijk aan 50';proceed}
when Int { say "is een Int";proceed}
when 42 { say 42 }
default { say "huh?" }
}
5.6. loop
loop is een andere manier om een for lus aan te geven.
In feite is loop precies zoals for lussen geschreven worden in de familie C-programmeertalen.
loop (my $i = 0; $i < 5; $i++) {
say "Het huidige nummer is $i"
}
5.7. while en until
De while lus herhaalt executie van een blok zolang de conditie waar is.
my $countdown = 3;
while $countdown > 0 {
say $countdown;
$countdown--;
}
say 'Gestart!';
Uitvoer3 2 1 Gestart!
until is tegenovergestelde versie van while. Het herhaalt het blok zolang de conditie niet waar is.
Dat is te vergelijken met unless.
Bovenstaand voorbeeld kan ook worden geschrene als
my $countdown = 3;
until $countdown == 0 {
say $countdown;
$countdown--;
}
say 'Gestart!';
| Je moet er wel voor zorgen dat de conditie wel op een gegeven ogenblik verandert, anders zal deze lus nooit tot een einde komen. |
5.8. next en last
next en last geven ons meer controle over een lus. Ze kunnen worden gebruikt in een for, loop, while en until lus.
-
nextnext negeert de rest van het blok en gaat verder met de volgende waarde -
lastspringt uit de lus.
for 1..10 -> $nummer {
next unless $nummer %% 2;
last if $nummer > 8;
say $nummer;
}
Uitvoer2 4 6 8
next unless $number %% 2 negeert de getallen die niet door 2 deelbaar zijn
last if $number > 8 stopt de lus zodra de waarde meer is dan 8, dus 10 zal nooit getoond worden.
| Zie https://docs.raku.org/language/control voor meer informatie over conditionele- en lusconstructies. |
6. I/O
De twee meest voorkomende manieren van Invoer/Uitvoer zijn Terminal en Bestanden.
6.1. Basic I/O op de Terminal
6.1.1. say
say schrijft naar de standaard uitvoer. Het voegt een regeleinde (newline) toe aan het einde. In andere woorden, de volgende code:
say 'Hallo mevrouw.';
say 'Hallo meneer.';
worden op 2 aparte lijnen getoond.
6.1.2. print
Aan de andere kant doet print precies hetzelfde, maar het voegt geen regeleinde toe.
Probeer eens om de say door een print te vervangen en vergelijk de resultaten.
6.1.3. get
Men kan get gebruiken om invoer van de terminal te krijgen.
my $naam;
say "Hoi, hoe heet je?";
$naam = get;
say "Welkom bij Raku, beste $naam";
Als je bovenstaande code uitvoert zal de terminal wachten tot je je naam intypt en op [Enter] drukt. Vervolgens zal het je begroeten.
6.1.4. prompt
prompt is een combinatie van print en get.
Het bovenstaande voorbeeld kan ook worden geschreven als:
my $naam = prompt "Hoi, hoe heet je? ";
say "Welkom bij Raku, beste $naam";
6.2. Uitvoeren van externe commando’s
Deze twee subroutines kunnen worden gebruikt om externe commando’s uit te voeren:
-
runvoert een extern commando direct uit. -
shellvoert een extern commando uit alsof je het hebt ingetypt op een commando regel (via een z.g. "shell"). Het hangt af van de systeem software die je gebruikt. Alle meta-karakters worden geïnterpreteerd door de shell, inclusief z.g. "pipes", "redirects" en specificaties van environment variabelen.
my $naam = 'Neo';
run 'echo', "hallo $naam";
shell "ls";
shell "dir";
echo en ls zijn veel voorkomende commando’s op Linux/OS X:
echo drukt de argumenten af (het equivalent van say in Raku)
ls laat alle bestanden en directories zien in de huidige directory
dir is het equivalent van ls bij Windows.
6.3. File I/O
6.3.1. slurp
Men kan slurp gebruiken om een geheel bestand in te lezen.
Maak een tekstbestand aan met de volgende inhoud:
Jan 9 Japie 7 Jolanda 8 Jessica 7
my $data = slurp "scores.txt";
say $data;
6.3.2. spurt
Men kan spurt gebruiken om data naar een bestand te schrijven.
my $nieuw = "Nieuwe scores:
Paul 10
Paulie 9
Paulo 11";
spurt "nieuwescores.txt", $nieuw;
Nadat je de bovenstaande code hebt uitgevoerd, bestaat er een bestand nieuwescores.txt . Dat zal dan de nieuwe scores bevatten.
6.3.3. lines
lines kan worden gebruikt om gegevens regel voor regel uit een bestand te lezen.
Laten we hetzelfde bestand datafile.txt lezen dat we ook met slurp hebben gebruikt.
for "datafile.txt".IO.lines -> $line {
say "Score: $line";
}
UitvoerScore: John 9 Score: Johnnie 7 Score: Jane 8 Score: Joanna 7
.IO verandert de string met de naam van een bestand in een object dat een bestand representeert.
.lines geeft de inhoud van het bestand regel voor regel, en de for lus gaat die dan één voor één behandelen.
6.4. Werken met bestanden en directories
6.4.1. Wat er in een directory zit
Raku kan de inhoud van een directory ook direct tonen zonder dat er externe commando’s voor hoeven te worden uitgevoerd, net zoals in een van de vorige voorbeelden.
say dir; # Laat bestanden/directories uit de huidige directory zien
say dir "/Documents"; # Laat bestanden/directories zien van de gegeven directory
6.4.2. Directories aanmaken en verwijderen
Tevens kun je ook nieuwe directories aanmaken en verwijderen.
mkdir "nieuwdir";
rmdir "nieuwdir";
mkdir maakt een nieuwe directory aan.
rmdir verwijdert een lege directory. Geeft een foutmelding terug indien niet leeg.
6.4.3. Bestandsinformatie
Je kunt ook kijken of een specifieke naam bestaat, en of het een bestand of een directory is:
Maak in de directory waar je dit script gaat uitvoeren een lege directory dir123 en een leeg bestand genaamd script123.raku
say "script123.raku".IO.e;
say "dir123".IO.e;
say "script123.raku".IO.d;
say "dir123".IO.d;
say "script123.raku".IO.f;
say "dir123".IO.f;
IO.e geeft terug of de naam bestaat.
IO.f geeft terug of het een bestand is.
IO.d geeft terug of het een directory is.
Gebruikers van Windows kunnen zowel de / als de \\ gebruiken om directories aan te makenC:\\rakudo\\binC:/rakudo/bin |
| Zie https://docs.raku.org/type/IO voor meer informatie over invoer en uitvoer. |
7. Subroutines
In dit hoofdstuk behandelen we hoe je subroutines definieert, hoe je waarden aan ze kunt sturen, en hoe je waarden terug kunt krijgen.
7.1. Definitie
Subroutines (ook wel subs of functies) zijn een manier om een stuk functionaliteit in een pakketje te stoppen.
De definitie van een subroutine begint met het sleutelwoord sub. Na de definitie kun je het aanroepen met de naam die je het gegeven hebt.
Bekijk onderstaand voorbeeld:
sub buitenaardse-groet {
say "Hallo aardlingen";
}
buitenaardse-groet;
Het vorige voorbeeld laat een subroutine zien die geen invoer nodig heeft.
7.2. Signatuur
Veel subroutines hebben een vorm van invoer nodig om hun werk te kunnen doen. Die invoer wordt gegeven door argumenten. Een subroutine mag 0 of meer parameters definiëren. Het aantal en het type van de parameters die door een subroutine worden gedefinieerd, noemen we de signatuur.
Onderstaande subroutine accepteert een string argument.
sub zeg-hallo (Str $naam) {
say "Hallo " ~ $naam ~ "!!!!"
}
zeg-hallo "Paul";
zeg-hallo "Paula";
7.3. Selectie bij aanroep
Het is mogelijk om meer dan één subroutine met dezelfde naam, maar met een verschillende signatuur, te definiëren.
Op het moment dat de subroutine wordt aangeroepen, zal de uitvoerder besluiten welke versie van de subroutine werkelijk zal worden aangeroepen, afhankelijk van het aantal en het type van de gegeven argumenten.
Dit soort subroutines wordt op dezelfde manier gedefinieerd als normale subroutines, maar in plaats van sub worden ze gedefinieerd met multi.
multi groet($naam) {
say "Goede morgen $naam";
}
multi groet($naam, $titel) {
say "Goede morgen $titel $naam";
}
groet "Jan";
groet "Laura", "Mevr.";
7.4. Default en Optionele Parameters
Als een subroutine is gedefinieerd om een argument te accepteren en we roepen het aan zonder dat benodigde argument, dan zal er een fout optreden.
Als alternatief biedt Raku de mogelijk om subroutines te definiëren met:
-
Optionele Parameters
-
Parameters met een verstek (default) waarde
7.4.1. Optionele parameters
Je kunt een optionele parameter aangeven door een ? achter de naam te plaatsen.
sub zeg-hallo($naam?) {
with $naam { say "Hallo " ~ $naam }
else { say "Hallo Mens" }
}
zeg-hallo;
zeg-hallo("Laura");
Als de gebruiker een bepaald argument niet meegeeft, dan wordt de eventuele default waarde van de parameter gebruikt.
Dit wordt aangegeven door een waarde toe te wijzen aan de parameter in de definitie van de subroutine.
sub zeg-hallo($naam = "Mens") {
say "Hallo " ~ $naam;
}
zeg-hallo;
zeg-hallo("Laura");
7.5. Genoemde parameters
De parameters die we tot nu toe hebben gezien, worden "positionele parameters" genoemd. Arguments worden toegekend aan parameters door hun positie: het eerst argument gaat naar de eerste parameter, het tweede argument naar de tweede parameter, en zo verder.
Een parameter die toegewezen wordt aan de hand van een naam, heet een "genoemde parameter".
sub groet(:$naam, :$titel) {
say "Goede morgen $titel $naam";
}
groet(naam => "Laura", titel => "Mrs.");
groet(titel => "Mr.", naam => "Pietje");
UitvoerGoed morgen Mrs. Laura Goed morgen Mr. Pietje
Since the arguments are matched by name, they can be supplied in any order. Omdat de argument per naam toegekend worden aan de parameter, maakt het niet uit in welke volgorde ze worden gespecificeerd.
|
Als de naam van een variabele overeenkomt met de naam van een genoemde parameter, dan kan de aanroep verkort worden:
|
7.6. Slurpende parameters
Een subroutine kan ook een variabel aantal argumenten opvangen in een slurpende parameter.
Een slurpende parameter is een array specificatie na een asterisk, zoals *@namen. Het verzamelt alle overige (positionele) argumenten.
sub groet-allen(*@namen) {
for @namen -> $naam {
say "Hallo $naam";
}
}
groet-allen("Paul");
groet-allen("Paul", "Paula", "Laura");
UitvoerHallo Paul Hallo Paul Hallo Paula Hallo Laura
7.7. Beperkingen
We hebben eerder al gezien dat een parameter beperkt kan worden tot een type, zoals Str $naam.
Het kan nog verder worden beperkt door het sleutelwoord where toe te voegen, gevolgd door een conditie.
sub register(Int $leeftijd where $leeftijd >= 18) {
say "Je bent registreerd";
}
register(21);
register(15);
UitvoerJe bent geregistreerd Constraint type check failed in binding to parameter '$leeftijd'; expected anonymous constraint to be met but got Int (15)
De eerste aanroep is succesvol. De tweede niet omdat 15 weliswaar een Int is, maar die waarde voldoet niet aan de conditie.
7.8. Teruggeven van waarden
Alle subroutines die we tot nu toe hebben gezien doen iets en laten dan het resultaat op het scherm zien.
Ook al is dit heel normaal, soms willen we dat een subroutine een waarde teruggeeft dat we later in het programma kunnen gebruiken.
7.8.1. Impliciete teruggave
Onder normal omstandigheden is de waarde van de laatste regel van een subroutine de waarde die door de subroutine terug wordt gegeven.
sub kwadrateer ($x) {
$x ** 2;
}
say "7 gekwadrateerd is gelijk aan " ~ kwadrateer(7);
7.8.2. Expliciete teruggave
Voor de duidelijkheid is het wellicht een goed idee om expliciet aan te geven wat we terug willen geven.
Dit kunnen we doen met het return sleutelwoord.
sub kwadrateer ($x) {
return $x ** 2;
}
say "7 gekwadrateerd is gelijk aan " ~ kwadrateer(7);
7.8.3. Beperken van mogelijke teruggegeven waarden
In een van de vorige voorbeelden hebben we gezien dat we parameters kunnen beperken tot een bepaald type. Hetzelfde kan worden gedaan met waarden die we teruggeven.
Om de teruggeven waarde te beperken tot een bepaald type, kunnen we de pijlnotatie --> in de signatuur gebruiken.
sub kwadrateer ($x --> Int) {
return $x ** 2;
}
say "1.2 gekwadrateerd is gelijk aan " ~ kwadrateer(1.2);
Als we de waarde voor teruggave niet van het juiste type is, zal er een foutmelding worden geproduceerd.
Type check failed for return value; expected Int but got Rat (1.44)
|
We kunnen niet alleen de typebeperking van de teruggeven waarde controleren; we kunnen ook laten controleren of het gedefinieerd is. In het vorige voorbeeld gaven we aan dat de teruggegeven waarde een Het is een goede gewoonte om dit soort typebeperkingen te gebruiken.
|
| Zie https://docs.raku.org/language/functions voor meer informatie over subroutines en functies. |
8. Functioneel Programmeren
In dit hoofdstuk gaan we kijken naar een aantal functionaliteiten die men kan gebruiken voor Functioneel Programmeren.
8.1. Functies zijn ook objecten.
Functies en subroutines zijn ook objecten, net als alle andere:
-
Ze kunnen worden doorgegeven als argument aan een subroutine
-
Ze kunnen worden teruggegeven als waarde door een subroutine
-
Ze kunnen worden toegekend aan een variabele
Een prachtig voorbeeld om dit concept te demonstreren is de map functie.
map is een zogenaamde hogere orde functie, want het accepteert een andere functie als argument.
my @array = <1 2 3 4 5>;
sub kwadrateer($x) {
$x ** 2
}
say map(&kwadrateer,@array);
(1 4 9 16 25)
We hebben een subroutine kwadrateer gedefinieerd die het argument tot de tweede macht verheft.
Vervolgens hebben we map aangeroepen met twee argumenten: een subroutine en een array.
Het resultaat is een lijst waaroin alle elementen van het array zijn gekwadrateerd.
Merk op dat als we een subroutine als argument willen doorgeven, we een & voor de naam moeten zetten.
8.2. Anonieme Functies
Een anonieme functie wordt ook wel een lambda genoemd.
Een anonieme functie is niet bekend onder een naam (want die heeft het niet).
8.2.1. Puntig blok
Laten we het map voorbeeld herschrijven met een anonieme functie
my @array = <1 2 3 4 5>;
say map(-> $x { $x ** 2 },@array);
Merk op dat in plaats van een subroutine te declareren en het door middel van de naam als argument aan map door te geven, we het direct in de aanroep definiëren.
De anonieme subroutine -> $x { $x ** 2 } kan niet als zodanig worden aangeroepen want het heeft geen naam.
In Raku noemen we deze notatie een "puntig blok" (pointy block).
8.2.2. Kortere specificatie
Een puntig blok dat maar 1 argument accepteert, kan op twee manier korter worden geschreven.
Een blok zonder → specificatie ontvangt één argument in de verstek-variabele $_
my @array = <1 2 3 4 5>;
say map({ $_ ** 2 }, @array);
Men kan ook een asterisk gebruiken om het argument aan te geven: in dat geval zijn de accolades niet nodig.
* ** 2 is een kortere specificatie van { $_ ** 2 }
my @array = <1 2 3 4 5>;
say map(* ** 2, @array);
Alledrie de notaties geven hetzelfde resultaat: (1 4 9 16 25)
8.2.3. Opslaan in een variabele
Een puntig blok kan ook aan een variabele worden toegekend:
my $kwadrateer = -> $x {
$x ** 2
}
say $kwadrateer(9);
8.3. Aankoppelen
In Raku kun je methoden aan elkaar koppelen, zodat je niet langer het resultaat van de aanroep van de ene subroutine als een argument aan een andere hoeft te geven.
Laten we het geval bekijken waarbij we een array met waarden kregen. Je wordt gevraagd om alle unieke (maar een keer voorkomende) waarden uit het array te halen en te sorteren van hoog naar laag.
Je zou dit probleem kunnen oplossen door iets te schrijven als:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @resultaat = reverse(sort(unique(@array)));
say @resultaat;
Eerst roepen we de unique functie aan op @array. Het resultaat daarvan geven we als argument aan sort en dan geven we het resultaat daarvan door aan reverse.
In tegenstelling tot bovenstaand voorbeeld mag je methodes aan elkaar koppelen in Raku.
Bovenstaand voorbeeld kan dus als volgt worden geschreven, waarbij we gebruik maken van het aankoppelen van methoden (method chaining):
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @resultaat = @array.unique.sort.reverse;
say @resultaat;
Je kunt zien dat het aankoppelen van methoden veel beter leest.
8.4. Aanvoer Operator
De aanvoer operator, ook wel pipe genoemd in sommige functionele programmeertalen, geeft een nog beter visualisatie van het aankoppelen van methoden.
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@array ==> unique()
==> sort()
==> reverse()
==> my @resultaat;
say @resultaat;
Start met `array` en geef een lijst van unieke elementen
en sorteer dat
en keer de volgorde om
en sla het resultaat daarvan op in @resultaat
Zoals je kunt zien is de volgorde van de aanroepen van de methoden van voor naar achter.
8.4.1. Achterwaardse Aanvoer
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @resultaat <== reverse()
<== sort()
<== unique()
<== @array;
say @resultaat;
De achterwaardse aanvoer is net als de voorwaardse aanvoer, maar dan andersom.
De volgorde van de aanroepen van de methoden is van achteren naar voren.
8.5. Hyper Operator
De hyper operator >>. roept de methode aan op elke element van een lijst en geeft een lijst terug met het resultaat van die aanroepen.
my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub is-even($var) { $var %% 2 };
say @array>>.is-prime;
say @array>>.&is-even;
Met de hyper operator kunnen we methoden aanroepen die standaard al in Raku zijn gedefinieerd, zoals is-prime dat ons vertelt of een getal een priemgetal is of niet.
Daarnaast kun je nieuwe subroutines definiëren en ze aanroepen met de hyper operator. In dat geval moeten we een & voor de naam plaatsen, bijvoorbeeld is-even
Dit is erg practisch want daardoor hoeven we geen for lus te schrijven om over elk element te itereren.
|
Raku garandeert dat de volgorde van de resultaten hetzelfde is als de volgorde van de originele waarden. Maar er is geen garantie dat Raku de methoden in de zelfde volgorde of in dezelfde thread zal uitvoeren. Men moet dus voorzichtig zijn bij het aanroepen van methoden met neveneffecten, zoals say of print (waarbij het neveneffect het tonen van de waarden is).
|
8.6. Juncties
Een junctie is een logische superpositie van waarden.
In onderstaand voorbeeld is 1 | 2 | 3 een junctie.
my $var = 2;
if $var == 1 | 2 | 3 {
say "De variabele is 1 of 2 of 3"
}
Het gebruik van juncties zorgt meestal voor zogenaamde autothreading; een opdracht wordt voor elk element van de junctie uitgevoerd en de resultaten daarvan worden in een nieuwe junctie opgeslagen en teruggegeven.
8.7. Luie Lijsten
Een luie lijst is een lijst die lamlendig wordt geëvalueerd.
Lamlendig evalueren stelt de evaluatie van een expressie uit totdat deze echt nodig is en probeert herhaalde evaluaties te voorkomen door resultaten op te slaan.
De voordelen zijn:
-
Verbeterde prestaties doordat onnodige berekeningen worden vermeden
-
De mogelijkheid om potentieel oneindige datastructuren aan te maken
-
De mogelijkheid om de besturingsstroom te definiëren
Om een luie lijst te maken gebruiken we de … infix operator.
Een luie lijst heeft een of meer startelementen, een generator en een eindpunt.
8.7.1. Simpele luie lijst
my $luielijst = (1 ... 10);
say $luielijst;
Het start element is 1 en het eindpunt is 10. Er is geen generator aangegeven, dus de default generator wordt gebruikt (+1).
In andere woorden, deze luie lijst zal de volgende waarden teruggeven (als daarom wordt gevraagd): (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
8.7.2. Oneindige luie lijst
my $luielijst = (1 ... Inf);
say $luielijst;
Deze luie lijst kan een getal tussen 1 en oneindig geven, in andere woorden alle natuurlijke getallen.
8.7.3. Luie lijst met een afgeleide generator
my $luielijst = (0,2 ... 10);
say $luielijst;
De startelementen zijn 0 en 2 en het eindpunt is 10.
Er is geen generator aangegeven, maar door de aangegeven startelementen kan Raku deduceren dat de generator (+2) is
Deze luie lijst zal de volgende waarden teruggeven (als daarom wordt gevraagd): (0, 2, 4, 6, 8, 10)
8.7.4. Luie lijst met een specifieke generator
my $luielijst = (0, { $_ + 3 } ... 12);
say $luielijst;
In dit voorbeeld specificeren we expliciet een generator tussen { }
Deze luie lijst zal de volgende waarden teruggeven (als daarom wordt gevraagd): (0, 3, 6, 9, 12)
|
Als men een expliciete generator specificeert moet het eindpunt één van de waarden zijn die door de generator terug kan worden gegeven. In plaats van Deze generator zal nooit stoppen
Deze generator zal wel stoppen
|
8.8. Omsluitingen
Alle code objecten in Raku zijn zogenaamde "omsluitingen" (closures), hetgeen betekent dat ze kunnen refereren aan lokale variabelen die zichtbaar zijn buiten het directe eigen bereik.
sub maak-begroeting {
my $naam = "Jan Jansen";
sub begroeting {
say "Goede morgen $naam";
};
return &begroeting;
}
my $groeter = maak-begroeting;
$groeter();
Indien je het bovenstaande programmaatje uitvoert, dan zal het Goede morgen Jan Jansen op het scherm tonen.
Dit is een tamelijk simpel voorbeeld. Het interessante aan dit voorbeeld is dat de begroeting (binnenste) subroutine teruggegeven were door de buitenste subroutine voordat het uitgevoerd werd.
$groeter is nu een omsluiting (closure) geworden.
Een omsluiting is een speciaal soort object dat twee zaken combineert:
-
Een subroutine
-
De omgeving waarin deze subroutine werd aangemaakt.
Die omgeving bestaat uit elke lokale variabele die "zichtbaar" (in-scope) was op het moment dat de closure werd aangemaakt.
In dit geval, $groeter is een closure waarin zich zowel de begroeting subroutine bevat als ook de Jan Jansen string die bestond toen de closure werd aangemaakt.
Laten we eens naar een interessanter voorbeeld kijken:
sub groeter-generator($periode) {
return sub ($naam) {
return "Goede $periode $naam"
}
}
my $smorgens = groeter-generator("morgen");
my $savonds = groeter-generator("avond");
say $smorgens("Jan");
say $savonds("Jane");
In dit voorbeeld hebben we een subroutine groeter-generator($periode) gemaakt die één enkel argument $periode verwacht, en een nieuwe subroutine teruggeeft. Deze subroutine verwacht één enkel argument $naam waarmee de begroeting wordt geconstrueerd die wordt teruggegeven.
In feite is groeter-generator een subroutine fabriek. In dit voorbeeld gebruiken we deze groeter-generator om twee nieuwe subroutines aan te maken. De ene zegt Goede morgen, en de andere zegt Goede avond.
$smorgens en $savonds zijn allebei closures. Ze delen dezelfde subroutine definitie, maar hebben verschillende omgevingen.
In de omgeving van $smorgens heeft $periode de waarde morgen. In de omgeving van $savonds heeft $periode de waarde avond.
9. Klassen en Objecten
In het vorige hoofdstuk hebben we geleerd hoe Raku het gemakkelijker maakt om Functioneel te Programmeren.
In dit hoofdstuk gaan we kijken naar het object georiënteerd programmeren in Raku.
Object Georiënteerd Programmeren is een van de programmeerstijl paradigma’s die tegenwoordig veel wordt gebruikt.
Een object is een verzameling van variabelen en subroutines die bij elkaar gevoegd zijn.
De variabelen noemen we attributen en de subroutines noemen we methoden.
Attributen bepalen de staat van een object, methoden bepalen het gedrag van het object.
Een klasse definieert de structuur van een verzameling objecten.
Bekijk onderstaand voorbeeld om deze relatie beter te begrijpen:
Er bevinden zich 4 personen in een kamer |
objecten ⇒ 4 personen |
Deze 4 personen zijn menselijk |
klasse ⇒ Mens |
Ze hebben een allen een naam, leeftijd, geslacht en nationaliteit |
attributen ⇒ naam, leeftijd, geslacht en nationaliteit |
In het taalgebruik van object georiënteerd programmeren zeggen we dat de objecten instanties zijn van een klasse.
9.1. Definiëren van een klasse
Bekijk het onderstaande script:
class Mens {
has $.naam;
has $.leeftijd;
has $.geslacht;
has $.nationaliteit;
}
my $jan = Mens.new(naam => 'Jan', leeftijd => 23, geslacht => 'M', nationaliteit => 'Nederlands');
say $jan;
Met het class sleutelwoord definieert men een klasse.
Met het has sleutelwoord definieert men een attribuut van een klasse.
De .new() methode wordt een constructeur genoemd. Het maakt een object als een instantie van de klasse waarop het wordt aangeroepen.
In het bovenstaande script, bevat de variabele $jan de nieuwe instantie van "Mens" aangemaakt met Mens.new().
De argumenten op naam die we aan de .new() methode meegeven, worden gebruikt om de attributen van het nieuwe object te initialiseren.
We kunnen een klasse ook een lexicaal gebied geven door middel van my:
my class Mens {
}
9.2. Inkapseling
Inkapseling is een concept uit het object georiënteerd programmeren waarmee we de bundeling van data en methoden aangeven.
De gegevens (attributen) binnen een object zouden privé moeten zijn. In andere woorden, alleen maar toegankelijk vanaf binnen in het object.
Om toegang te verlenen aan de attributen vanaf buiten het object, gebruiken we methoden die we accessors noemen.
Onderstaande scripts geven hetzelfde resultaat.
my $var = 7;
say $var;
my $var = 7;
sub geef-var {
$var;
}
say geef-var;
De subroutine geef-var kan men als een accessor beschouwen. Het maakt het mogelijk om aan de waarde van de variabele te komen zonder er direct toegang daarvoor nodig te hebben.
Inkapseling wordt in Raku mogelijk gemaakt door middel van zogenaamde twigils.
Twigils zijn secondaire sigils. Ze worden tussen de sigil en de naam van het attribuut geplaatst.
Bij klassen kunnen twee sigils worden gebruikt:
-
!om aan te geven dat een attribuut privé is. -
.om aan te geven dat een accessor voor de attribuut aangemaakt moet worden.
Als men geen twigil meegeeft, is een attribuut privé. Voor de leesbaarheid is het een goede gewoonte om in zo’n geval altijd de ! twigil te gebruiken.
Met wat we zojuist gezien hebben, zouden we bovenstaande klasse moeten herschrijven als:
class Mens {
has $!naam;
has $!leeftijd;
has $!geslacht;
has $!nationaliteit;
}
my $jan = Mens.new(naam => 'Jan', leeftijd => 23, geslacht => 'M', nationaliteit => 'Nederlands');
say $jan;
Voeg het volgende commando to: say $jan.leeftijd
Het zal de volgende fout geven: Method 'leeftijd' not found for invocant of class 'Mens'
De reden hiervoor is dat $!leeftijd privé is en daardoor alleen maar binnen het object gebruikt kan worden.
Pogingen om aan het attribuut te komen van buiten het object zal je een foutmelding geven.
Vervang nu has $!leeftijd with has $.leeftijd en bekijk dan het resultaat van say $jan.leeftijd;
9.3. Genoemde en positionele argumenten
Alle klassen hebben beschikking over een .new() constructeur door overerving.
Het kan worden gebruikt om objecten aan te maken door het argumenten te geven.
De default constructeur accepteert alleen genoemde argumenten.
Als je bovenstaande voorbeelden bekijkt, dan zul je zien dat alle argumenten die we aan .new() hebben gegeven, op naam zijn:
-
naam => 'Jan'
-
leeftijd => 23
Wat als ik niet telkens de naam van elk attribuut wil meegeven als ik een nieuw object wil maken?
In dat geval moeten we een andere constructeur maken die positionele argumenten accepteert.
class Mens {
has $.naam;
has $.leeftijd;
has $.geslacht;
has $.nationaliteit;
#nieuwe constructeur die de default constructeur vervangt voor deze klasse
method new ($naam, $leeftijd, $geslacht, $nationaliteit) {
self.bless(:$naam, :$leeftijd, :$geslacht, :$nationaliteit);
}
}
my $jan = Human.new('Jan',23,'M','Nederlands');
say $jan;
9.4. Verplichte attributen en attributen met verstekwaarde
We hebben eerder al gezien dat een subroutine niet aangeroepen kan worden zonder een van haar parameters.
De standaard constructor gedraagt zich anders: een attribuut waarvoor geen genoemd argument is meegegeven in een aanroep aan .new() blijft simpelweg gezien leeg.
Net als met parameters aan een subroutine, is het mogelijk om een verstekwaarde aan een attribuut toe te kennen.
Like the parameters of a subroutine, an attribute can be given a default value.
Dit gebeurt door een toewijzing aan te geven bij de definitie.
Andersom, als er altijd een genoemd argument moet worden gegeven voor een attribuut, dan kan dat worden aangeven met de sleutelwoorden is required
class Mens {
has $.naam is required;
has $.nationaliteit = 'Onbekend';
}
my $jan = Menw.new(name => 'Jan');
say $jan.nationality;
my $niemand = Human.new;
UitvoerOnbekend The attribute '$!naam' is required, but you did not provide a value for it.
The nationaliteit van $jan werd niet aangegeven, dus werd de verstekwaarde gebruikt.
$niemand kon niet worden gemaakt omdat het genoemde argument naam ontbrak.
9.5. Methoden
Methoden zijn de subroutines van een object.
Zij zijn, net als subroutines, een manier om een aantal functies te bundelen, ze accepteren argumenten, hebben een signatuur en kunnen worden gedefinieerd als multi.
Je definieert een methode met het method sleutelwoord.
Normaal gesproken zijn methoden nodig om bepaalde acties op de attributen van een object uit te voeren.
Dit bekrachtigt het concept van inkapseling. De attributen van een object kunnen alleen worden gemanipuleerd vanaf binnen de klasse van het object door gebruik van methoden.
De buitenwereld kan alleen maar gebruik maken van de methoden van het object en heeft geen toegang tot de attributen van het object.
class Mens {
has $.naam;
has $.leeftijd;
has $.nationaliteit;
has $.geschiktheid;
method bepaal-geschiktheid
if self.leeftijd < 21 {
$!geschiktheid = 'Nee'
} else {
$!geschiktheid = 'Ja'
}
}
}
my $jan = Mens.new(naam => 'Jan', leeftijd => 23, nationaliteit => 'Nederlands');
$jan.bepaal-geschiktheid;
say $jan.geschiktheid;
Door een methode te definiëren in een klasse, kun je deze aanroepen op een object met de punt notatie:
object . methode of als in bovenstaand voorbeeld: $jan.bepaal-geschiktheid
Binnen de definitie van een methode kunnen we het sleutelwoord self gebruiken om te refereren aan het object zelf om andere methodes aan te roepen.
Binnen de definitie van een methode kunnen we direct een attribuut gebruiken door de ! twigil te gebruiken, zelfs als het attribuut is gedefinieerd met de . twigil.
De reden daarachter is dat wat de . twigil doet is een attribuut met ! te definiëren en automatisch te zorgen voor het aanmaken van een accessor.
In bovenstaand voorbeeld hebben if self.leeftijd < 21 en if $!leeftijd < 21 hetzelfde effect, maar technisch gezien zijn ze verschillend:
-
self.leeftijdroept de.leeftijdmethode (accessor) aan
Hetgeen overigens ook geschreven can worden als$.leeftijd -
$!leeftijdis een directe toegang tot de variabele
9.5.1. Privé methoden
Normale methoden kunnen op objecten buiten de klasse zelf worden aangeroepen.
Privé methoden zijn methoden die alleen maar kunnen worden aangeroepen binnen een klasse.
Dit kan bijvoorbeeld gebruikt worden als een methode een andere methode moet aanroepen voor een specifieke actie.
De methode die gebruikt kan worden vanaf de buitenwereld is publiek, terwijl de andere methode onzichtbaar moet blijven.
Aangezien we niet willen dat gebruikers die methode direct kunnen aanroepen, definiëren we het als een privé methode.
Door een ! voor de naam van een methode te plaatsen, geven we aan dat het om een privé methode gaat.
Privé methoden moeten worden aangeroepen met een ! in plaats van met .
method !ikbenprivé {
#code waar het over gaat
}
method ikbenpubliek {
self!ikbenprivé;
#doe extra dingen
}
9.6. Klasse Attributen
Klasse Attributen zijn attributen die bij de klasse zelf horen en niet bij de objecten van die klasse.
Zij kunnen worden geïnitialiseerd bij de definitie.
Klasse attributen worden gedefinieerd met my in plaats van met has.
Zij worden aangeroepen op de klasse zelf in plaats van op haar objecten.
class Mens {
has $.naam;
my $.aantal = 0;
method new($naam) {
Mens.aantal++;
self.bless(:$naam);
}
}
my $a = Mens.new('a');
my $b = Mens.new('b');
say Mens.aantal;
9.7. Toegangssoorten
In alle voorbeelden die we tot nu toe hebben gezien, hebben we accessors alleen maar gebruikt om informatie uit een object te halen.
Maar wat nu als we de waarde van een attribuut willen veranderen?
In dat geval moeten we die attribuut als lees/schrijf markeren met de sleutelwoorden is rw (read/write).
class Mens {
has $.naam;
has $.leeftijd is rw;
}
my $jan = Mens.new(naam => 'Jan', leeftijd => 21);
say $jan.leeftijd;
$jan.leeftijd = 23;
say $jan.leeftijd;
Als je bij de definitie niets aangeeft, wordt een attribuut als alleen lezen gedefinieerd, maar je kunt ook expliciet is readonly aangeven.
9.8. Overerving
Overerving is een concept van het object georiënteerd programmeren.
Wanneer men klassen aan het definiëren is, komt men er snel genoeg achter dat sommige attributen/methoden in vele klassen voorkomen.
Moeten we dus maar code gaan dupliceren?
NEE! We moeten gebruik maken van overerving.
Laten we aannemen dat we twee klassen willen definiëren, een klasse voor Mensen en een klasse voor Werknemers.
Mensen hebben twee attributen: naam en leeftijd.
Werknemers hebben 4 attributen: naam, leeftijd, bedrijf en salaris
Men zou geneigd kunnen zijn om de klassen als volgt te definiëren:
class Mens {
has $.naam;
has $.leeftijd;
}
class Werknemer {
has $.naam;
has $.leeftijd;
has $.bedrijf;
has $.salaris;
}
Hoewel bovenstaande code technisch gezien correct is, is het qua concept van lage kwaliteit.
Een betere manier om zoiets te schrijven is:
class Mens {
has $.naam;
has $.leeftijd;
}
class Werknemer is Mens {
has $.bedrijf;
has $.salaris;
}
Het is sleutelwoord definieert de overerving.
In het taalgebruik van object georiënteerd programmeren, zeggen we dat Werknemer een kind is van Mens, en dat Mens de ouder is van Werknemer.
Alle kinderklassen erven de attributen en methoden van de ouderklasse, zodat het niet nodig is om deze opnieuw te definiëren.
9.8.1. Overnemen
Klassen erven alle attributen en methoden van hun ouderklas.
Er zijn gevallen waarin we willen det de methode in een kinderklasse zich anders gedraagt als methode die geërfd is van de ouderklasse.
Om dit te bereiken, definiëren we die methode ook in de kinderklasse.
We noemen dit concept overnemen (overriding).
In het onderstaande voorbeeld wordt de method stel-jezelf-voor geërfd door de Werknemer klasse.
class Mens {
has $.naam;
has $.leeftijd;
method stel-jezelf-voor {
say 'Hoi, ik ben een mens en mijn naam is ' ~ self.naam;
}
}
class Werknemer is Mens {
has $.bedrijf;
has $.salaris;
}
my $jan = Mens.new(naam =>'Jan', leeftijd => 23,);
my $jessica = Werknemer.new(naam =>'Jessica', leeftijd => 25, bedrijf => 'Bureco', salaris => 4000);
$jan.stel-jezelf-voor;
$jessica.stel-jezelf-voor;
Het overnemen werkt als volgt:
class Mens {
has $.naam;
has $.leeftijd;
method stel-jezelf-voor {
say 'Hoi, ik ben een mens en mijn naam is ' ~ self.naam;
}
}
class Werknemer is Mens {
has $.bedrijf;
has $.salaris;
method stel-jezelf-voor {
say 'Hoi, ik ben een werknemer, mijn naam is ' ~ self.naam ~ ' en ik werk bij: ' ~ self.bedrijf;
}
}
my $jan = Mens.new(naam =>'Jan', leeftijd => 23,);
my $jessica = Werknemer.new(naam =>'Jessica', leeftijd => 25, bedrijf => 'Bureco', salaris => 4000);
$jan.stel-jezelf-voor;
$jessica.stel-jezelf-voor;
Afhankelijk van de klasse van het object, zal de juiste methode worden aangeroepen.
9.8.2. Submethoden
Submethoden zijn een soort methoden die niet worden geërfd door kinderklassen.
Ze zijn alleen toegankelijk in de klasse waarin ze worden gedefinieerd.
Ze worden gedefinieerd met het submethod sleutelwoord.
9.9. Meervoudige Overerving
Meervoudige overerving is toegestaan in Raku. Een klasse kan van meer dan één andere klasse erven.
class staafdiagram {
has Int @.staaf-waarden;
method plot {
say @.staaf-waarden;
}
}
class lijndiagram{
has Int @.lijn-waarden
method plot {
say @.lijn-waarden
}
}
class combo-diagram is staafdiagram is lijndiagram {
}
my $verkocht = staafdigram.new(staaf-waarden => [10,9,11,8,7,10]);
my $voorspeld = lijndiagram.new(lijn-waarden => [9,8,10,7,6,9]);
my $verkocht-vs-voorspeld =
combo-diagram.new(staaf-waarden => [10,9,11,8,7,10],
lijn-waarden => [9,8,10,7,6,9]);
say "Verkocht:";
$verkocht.plot;
say "Voorspeld:";
$voorspeld.plot;
say "Verkocht vs Voorspeld:";
$verkocht-vs-voorspeld.plot;
UitvoerVerkocht: [10 9 11 8 7 10] Voorspeld: [9 8 10 7 6 9] Verkocht vs Voorspeld: [10 9 11 8 7 10]
De combo-diagram klasse zou in staat moeten zijn om de twee reeksen van waarden, een van "verkocht" in een staafdiagram en een van "voorspeld" in het lijndiagram, te tonen.
Dat is de reden waarom we het hebben gedefinieerd als een kinderklasse van lijndiagram en staafdiagram.
Je hebt waarschijnlijk gemerkt dat het aanroepen van de methode plot op het combo-diagram niet het gewenste resultaat gaf.
Slechts één reeks van waarden werd getoond.
Waarom gebeurde dit?
combo-diagram erft van lijndiagram en staafdiagram en beide hebben ze een methode die plot heet.
Als we die methode op combo-diagram aanroepen zal Raku dit conflict proberen op te lossen door één van de geërfde methoden aan te roepen.
Om ervoor te zorgen dat dit correct functioneert, hadden we een methode plot in de combo-diagram klasse moeten definiëren.
class staafdiagram {
has Int @.staaf-waarden;
method plot {
say @.staaf-waarden;
}
}
class lijndiagram{
has Int @.lijn-waarden
method plot {
say @.lijn-waarden
}
}
class combo-diagram is staafdiagram is lijndiagram {
method plot {
say @.staaf-waarden;
say @.lijn-waarden;
}
}
my $verkocht = staafdigram.new(staaf-waarden => [10,9,11,8,7,10]);
my $voorspeld = lijndiagram.new(lijn-waarden => [9,8,10,7,6,9]);
my $verkocht-vs-voorspeld =
combo-diagram.new(staaf-waarden => [10,9,11,8,7,10],
lijn-waarden => [9,8,10,7,6,9]);
say "Verkocht:";
$verkocht.plot;
say "Voorspeld:";
$voorspeld.plot;
say "Verkocht vs Voorspeld:";
$verkocht-vs-voorspeld.plot;
UitvoerVerkocht: [10 9 11 8 7 10] Voorspeld: [9 8 10 7 6 9] Verkocht vs Voorspeld: [10 9 11 8 7 10] [9 8 10 7 6 9]
9.10. Rollen
Een Rol is net als een klasse verzameling van attributen en methoden.
Rollen kunnen worden gedefinieerd met het sleutelwoord role en klassen die een rol willen "spelen" kunnen dat aangeven met het does sleutelwoord.
role staafdiagram {
has Int @.staaf-waarden;
method plot {
say @.staaf-waarden;
}
}
role lijndiagram{
has Int @.lijn-waarden
method plot {
say @.lijn-waarden
}
}
class combo-diagram does staafdiagram does lijndiagram {
method plot {
say @.staaf-waarden;
say @.lijn-waarden;
}
}
my $verkocht = staafdigram.new(staaf-waarden => [10,9,11,8,7,10]);
my $voorspeld = lijndiagram.new(lijn-waarden => [9,8,10,7,6,9]);
my $verkocht-vs-voorspeld =
combo-diagram.new(staaf-waarden => [10,9,11,8,7,10],
lijn-waarden => [9,8,10,7,6,9]);
say "Verkocht:";
$verkocht.plot;
say "Voorspeld:";
$voorspeld.plot;
say "Verkocht vs Voorspeld:";
$verkocht-vs-voorspeld.plot;
Voer het bovenstaande script uit en je zult zien dat de resultaten hetzelfde zijn.
Nu zul je jezelf afvragen: als rollen net als klassen werken, wat is dan hun nut?
Om deze vraag te beantwoorden passen we het eerste script dat we gebruikten om meervodige overerving te laten zien,
degene waarin we hadden vergeten om de plot methode te definiëren.
role staafdiagram {
has Int @.staaf-waarden;
method plot {
say @.staaf-waarden;
}
}
role lijndiagram{
has Int @.lijn-waarden
method plot {
say @.lijn-waarden
}
}
class combo-diagram does staafdiagram does lijndiagram {
}
my $verkocht = staafdigram.new(staaf-waarden => [10,9,11,8,7,10]);
my $voorspeld = lijndiagram.new(lijn-waarden => [9,8,10,7,6,9]);
my $verkocht-vs-voorspeld =
combo-diagram.new(staaf-waarden => [10,9,11,8,7,10],
lijn-waarden => [9,8,10,7,6,9]);
say "Verkocht:";
$verkocht.plot;
say "Voorspeld:";
$voorspeld.plot;
say "Verkocht vs Voorspeld:";
$verkocht-vs-voorspeld.plot;
Uitvoer===SORRY!=== Method 'plot' must be resolved by class combo-diagram because it exists in multiple roles (lijndiagram, staafdiagram)
Als een klasse meer dan één rol doet en er daarbij een conflict optreedt, dan zal de compiler een foutmelding geven.
Dit is een veel veiligere aanpak dan meervoudige overerving waarbij conflicten niet als een probleem worden gezien en er bij de uitvoering zo maar iets gedaan wordt.
Rollen waarschuwen je wanneer er een conflict is.
9.11. Introspectie
Introspectie is het verkrijgen van informatie over de eigenschappen van een object, zoals het type, haar attributen of haar methoden.
class Mens {
has $.naam;
has $.leeftijd;
method stel-jezelf-voor {
say 'Hoi, ik ben een mens en mijn naam is ' ~ self.naam;
}
}
class Werknemer is Mens {
has $.bedrijf;
has $.salaris;
method stel-jezelf-voor {
say 'Hoi, ik ben een werknemer, mijn naam is ' ~ self.naam ~ ' en ik werk bij: ' ~ self.bedrijf;
}
}
my $jan = Mens.new(naam =>'Jan', leeftijd => 23,);
my $jessica = Werknemer.new(naam =>'Jessica', leeftijd => 25, bedrijf => 'Bureco', salaris => 4000);
say $jan.WHAT;
say $jessica.WHAT;
say $jan.^attributes;
say $jessica.^attributes;
say $jan.^methods;
say $jessica.^methods;
say $jessica.^parents;
if $jessica ~~ Mens { say 'Jessica is een Mens'};
Introspectie is mogelijk door:
-
.WHATgeeft de klasse van het object. -
.^attributesgeeft een lijst met attributen van het object. -
.^methodsgeeft een lijst van alle methoden die op het object kunnen worden uitgevoerd. -
.^parentsgeeft een lijst met alle ouderklassen van het object. -
~~wordt de smart-match operator genoemd. Het geeft de waarde True als het object is aangemaakt met de gegeven klasse, of met een van de ouderklassen van de gegeven klasse.
|
Voor meer informatie over Object Georiënteerd Programmeren in Raku, bekijk dan: |
10. Exception Handling
Exceptions zijn de uitzonderingsgevallen die optreden bij het uitvoeren van een programma op het moment dat er iets fout gaat.
We zeggen dat exceptions worden geworpen (thrown).
Bekijk onderstaand script dat correct kan worden uitgevoerd:
my Str $naam;
$naam = "Joanna";
say "Hallo " ~ $naam;
say "Hoe gaat het?"
UitvoerHallo Joanna Hoe gaat het?
Bekijk nu onderstaand script dat een exception werpt:
my Str $naam;
$naam = 123;
say "Hallo " ~ $naam;
say "Hoe gaat het?"
UitvoerType check failed in assignment to $naam; expected Str but got Int in block <unit> at bestandsnaam.raku:2
Je zou hebben moeten zien dat zodra er een fout optreedt (in dit geval het toewijzen van een getal aan een string variabele) het programma stopt en de regels code na de fout niet worden uitgevoerd, zelfs als deze wel juist zijn.
10.1. Opvangen van Exceptions
Exception handling is het proces van het opvangen van de exceptions die worden geworpen om ervoor te zorgen dat het script blijft werken.
Er zijn twee manieren om een exception op te vangen op de plaatsen in je code waar je die verwacht.
10.1.1. try
Indien je alleen maar zeker wilt zijn dat een exception niet je programma beëindigt, en je bent niet geïnteresseerd in de reden, dan kun je die code omgeven in een try blok.
my Str $naam;
try {
$naam = 123;
say "Hallo " ~ $naam;
}
say "Hoe gaat het?"
UitvoerHoe gaat het?
Indien de code in een try blok een exception werpt, dan wordt die
exception opgevangen, en de waarde Nil wordt dan teruggegeven in plaats
van de laatste waarde in het blok.
Het exception object wordt opgeslagen in the speciale variabele $!.
Als er niets misging in het blok, dan krijgt deze de waarde Nil en
het if blok wordt dan overgeslagen in onderstaand voorbeeld:
my Str $naam;
try {
$naam = 123;
say "Hello " ~ $naam;
}
if $! {
say "Er is iets misgegaan: " ~ $!.message;
}
say "Hoe gaat het?";
UitvoerEr is iets misgegaan: Type check failed in assignment to $name; expected Str but got Int (123) Hoe gaat het?
10.1.2. CATCH
De andere manier om exceptions op te vangen, is met een CATCH blok.
De code in een CATCH blok wordt uitgevoerd zodra er een exception wordt geworpen in een blok waarin zich een CATCH blok bevindt.
Elk blok kan maar één CATCH blok hebben.
Het CATCH blok kan op elke plaats in een blok geplaatst worden. Meestal
is het het beste om dat aan het begin van een blok te doen, zoday iemand die
de code moet lezen direct kan zien dat exceptions een speciale behandeling
krijgen in dat blok.
try {
CATCH {
default {
# deze code zal alleen uitgevoerd worden als een exception is geworpen
}
}
# voer hier de code in
# als er iets fout gaat, zal de code in het CATCH block uitgevoerd worden
# als alles goed gaat, wordt de code in het CATCH block genegeerd
}
10.2. Types van exceptions
Het CATCH block kan worden gedefinieerd op dezelfde manier als een given block.
Dat betekent dat we verschillende soorten van exceptions kunnen opvangen en afhandelen.
try {
CATCH {
when X::AdHoc { # doe iets als een exception van het type X::AdHoc is geworpen }
when X::IO { # doe iets als een exception van het type X::IO is geworpen }
when X::OS { # doe iets als een exception van het type X::OS is geworpen }
default { # doe iets als een ander soort exception is geworpen }
}
# voer hier de code in
# als er iets fout gaat, zal de code in het CATCH block uitgevoerd worden
# als alles goed gaat, wordt de code in het CATCH block genegeerd
}
Alle getypeerde exceptions erven van de klasse Exception. De namen van de altijd beschikbare exception klassen beginnen met X::, hieronder zie je een paar voorbeelden:
X::AdHoc is het simpelste exception type
X::IO is gerelateerd aan IO fouten
X::OS is gerelateerd aan OS fouten
X::Str::Numeric wordt geworpen bij een fout in het veranderen van een string naar een getal.
| Zie https://docs.raku.org/type-exceptions.html voor een complete lijst van exception types en de daarbij behorende methoden. |
10.3. Uitleg van exceptions
Binnen een CATCH blok wordt de exception geplaatst in de verstek-variabele
$_
Een aanroep van de .message methode geeft dan een bescrijving van de fout die het werpen van de exception veroorzaakte.
my Str $naam;
{
CATCH {
default {
say "Er ging iets fout: " ~ .message;
}
}
$naam = 123;
say "Hallo " ~ $naam;
}
say "Hoe gaat het?";
UitvoerEr ging iets fout: Type check failed in assignment to $name; expected Str but got Int (123) Hoe gaat het?
10.4. Het Werpen Van Exceptions
Naast het vangen van exceptions, laat Raku het ook toe om een exception expliciet te werpen.
Man kan twee soorten van exceptions werpen:
-
ad-hoc exceptions
-
getypeerde exceptions
my Int $leeftijd = 21;
die "Fout !";
my Int $age = 21;
X::AdHoc.new(payload => 'Fout !').throw;
Ad-hoc exceptions worden geworpen door de die subroutine gevolgd door de uitleg van de exception.
Getypeerde exceptions zijn objecten, daarom moeten we de .new() constructeur gebruiken in bovenstaand voorbeeld.
11. Reguliere Expressies
Een reguliere expressie, of regex, is een reeks van karakters die worden gebruikt voor patroonherkenning.
De gemakkelijkste manier om dit te begrijpen, is om het te beschouwen als een patroon.
if 'verlichting' ~~ m/ licht / {
say "verlichting bevat het woord licht";
}
In dit voorbeeld werd de smart match operator ~~ gebruikt om te kijken of een string (verlichting) het woord (licht) bevat.
"Verlichting" wordt vergeleken met de regex m/ light /
11.1. Definiëren van een Regex
Een reguliere expressie kan als volgt worden gedefinieerd:
-
/licht/ -
m/licht/ -
rx/licht/
Tenzij het specifiek wordt aangeduid, is witruimte zonder betekenis, dus m/licht/ en m/ licht / zijn hetzelfde.
11.2. Vergelijken van karakters
Alphanumerieke karakters en het liggend streepje _ mogen als zichzelf worden geschreven.
Alle andere karakters moeten speciaal worden behandeld door er een backslash \\ voor te plaatsen of door ze te omgeven door aanhalingstekens.
if 'Temperatuur: 13' ~~ m/ \: / {
say "De string bevat een dubbele punt :";
}
if 'Leeftijd = 13' ~~ m/ '=' / {
say "De string bevat een is-gelijk teken =";
}
if 'naam@bedrijf.nl' ~~ m/ "@" / {
say "Dit is een juist email-adres want het bevat een @ karakter";
}
11.3. Vergelijken van categorieën van karakters
Karakters kunnen worden geclassificeerd in categorieën en ook daarmee kunnen we vergelijken.
We kunnen ook vergelijken met de inverse van een categorie (alle karakters behalve degene in die categorie).
Categorie |
Regex |
Inverse |
Regex |
Woord karakter (letter, cijfer or liggend streepje) |
\w |
Alle karakters behalve een woord karakter |
\W |
Cijfer |
\d |
Elk karakter die geen cijfer zijn |
\D |
Witruimte |
\s |
Elk karakter dat geen witruimte is |
\S |
Horizontale witruimte |
\h |
Elk karakter dat geen horizontale witruimte is |
\H |
Verticale witruimte |
\v |
Elk karakter dat geen verticale witruimte is |
\V |
Tab |
\t |
Elk karakter behalve de Tab |
\T |
Nieuwe regel |
\n |
Elk karakter dat geen nieuwe regel aangeeft |
\N |
if "Jan123" ~~ / \d / {
say "Deze naam is niet toegestaan want het bevat cijfers";
} else {
say "Deze naam is toegestaan"
}
if "Jan-Met" ~~ / \s / {
say "Deze string bevat witruimte";
} else {
say "Deze string bevat geen witruimte"
}
11.4. Unicode eigenschappen
Het vergelijken van categorieën van karakters zoals we in de vorige sectie zagen, is erg gemakkelijk.
Maar wellicht is het gebruik van Unicode eigenschappen een meer systematische aanpak.
Unicode eigenschappen worden omgeven door <: >
if "Devanagari cijfers १२३" ~~ / <:N> / {
say "Bevat een cijfer";
} else {
say "Bevat geen enkel cijfer"
}
if "Привет, Иван." ~~ / <:Lu> / {
say "Bevat een hoofdletter";
} else {
say "Bevat geen enkele hoofdletter"
}
if "Jan-Met" ~~ / <:Pd> / {
say "Bevat een streepje";
} else {
say "Bevat geen enkel streepje"
}
11.5. Jokers
Jokers (wildcards) kunnen ook in een regex worden gebruikt.
De punt . betekent elk enkel karakter.
if 'abc' ~~ m/ a.c / {
say "Komt overeen";
}
if 'a2c' ~~ m/ a.c / {
say "Komt overeen";
}
if 'ac' ~~ m/ a.c / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.6. Factoren
Factoren (quantifiers) komen na een karakter en worden gebruikt om aan te geven hoe vaak we dat karakter verwachten.
11.6.1. Nul of één keer
Het vraagteken ? betekent nul of één keer.
if 'ac' ~~ m/ a?c / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'c' ~~ m/ a?c / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.6.2. Nul of meer keer
De asterisk * betekent nul of meer keer.
if 'az' ~~ m/ a*z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'aaz' ~~ m/ a*z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'z' ~~ m/ a*z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.6.3. Één of meer keer
Het plusteken + betekent tenminste één keer.
if 'az' ~~ m/ a+z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'aaz' ~~ m/ a+z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'z' ~~ m/ a+z / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.7. Verankering
Een regex kan overal in een string overeenkomen. Ankers verbinden een overeenkomst aan een bepaalde positie in de string.
-
^verankert aan het begin van de string. -
$verankert aan het einde van de string.
if 'raku gids' ~~ m/ ^ raku / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'de raku gids' ~~ m/ ^ raku / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'de raku gids' ~~ m/ gids $ / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.8. Alternatieven
Het | symbool ("vertical bar") betekent of. Het kan worden gebruikt om twee of meer alternatieven aan te geven.
if 'raku.org' ~~ m/ com | org | net / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
if 'raku.rocks' ~~ m/ com | org | net / {
say "Komt overeen";
} else {
say "Komt niet overeen";
}
11.9. Resultaten van een Vergelijking
Elke keer wanneer het vergelijken van een string met een regex succesvol is, wordt het resultaat in een speciale variable $/ geplaatst.
if 'Rakudo is een Raku compiler waarmee...' ~~ m/:s Raku compiler / {
say "Het resultaat is: " ~ $/;
say "De string vóór de gevonden string is: " ~ $/.prematch;
say "De string achter de gevonden string is: " ~ $/.postmatch;
say "De gevonden string begint op positie: " ~ $/.from;
say "De gevonden string eindigt één positie voor: " ~ $/.to;
}
Het resultaat is: 「Raku-compiler」 De string vóór de gevonden string is: Rakudo is een De string achter de gevonden string is: waarmee... De gevonden string begint op positie: 14 De gevonden string eindigt één positie voor: 27
$/ geeft een Match Object terug (de string die gevonden is door de regex)
Deze methoden kunnen worden aangeroepen op een Match Object:
.prematch geeft de string vóór de gevonden string terug.
.postmatch geeft de string na de gevonden string terug.
.from geeft de positie van het eerste karakter van de gevonden string terug.
.to geeft de positie nà het laatste karakter van de gevonden string terug.
|
Witruimte heeft per default geen betekening in een regex. Als we willen vergelijken met een regex die witruimte bevat, dan moeten we dat expliciet aangeven. De :s in de regex m/:s Raku/ geeft aan dat witruimte niet moet worden genegeerd.We zouden de regex ook kunnen hebben schrijven als m/ Raku \s compiler / waarbij we \s als een teken voor witruimte hebben gebruikt, zoals we eerder hebben gezien.Als een regex meer dan één witruimte bevat, dan is het gebruik van :s gemakkelijker in plaats van \s te specificeren voor elke witruimte.
|
11.10. Uittreksels
Indien men een gedeelte van een regex omgeeft met haken, dan vangt men dat gedeelte van de string in een uittreksel.
Uittreksels zijn genummerd vanaf nul. Ze zijn beschikbaar in de variabelen $0, $1, $2 enzovoort.
Scriptif 'Jan is 23 jaar' ~~ m/ (\w+) \s is \s (\d+) / {
say "Naam: $0";
say "Leeftijd: $1";
}
UitvoerName: Jan Leeftijd: 23
(\w+) is het eerste uittreksel. Het komt overeen met Jan en wordt in $0 opgeslagen
(\d+) is het tweede uittreksel. Het komt overeen met 23 en wordt in $1 opgeslagen
11.11. Substitutie
Een regex kan ook worden
A regex can also be used to replace the part of a string that it matches.
This is done using the .subst method. It is given a regex and a replacement, and it returns a new string.
Scriptmy $tekst = 'Ik drink thee, thee is goed';
say $tekst.subst(/thee/, 'koffie');
say $tekst.subst(/thee/, 'koffie', :g);
say $tekst;
UitvoerIk drink koffie, thee is goed Ik drink koffie, koffie is goed Ik drink thee, thee is goed
.subst vervangt alleen de eerste overeenkomst.
Door het toevoegen van :g (kort voor globaal) worden alle overeenkomsten vervangen.
.subst verandert de onderliggende string niet, zoals de laatste regel toont.
11.12. Voorbeeld
Laten we eens kijken of een email-adres goed is of niet.
Voor dit voorbeeld zullen we aannemen dat een werkend email-adres wordt gevormd door:
voornaam [punt] achternaam [bij] bedrijfsnaam [punt] top-niveau (e.g. nl)
|
De regex die in dit voorbeeld wordt gebruikt voor het valideren van een email-adres is niet erg accuraat. Het is alleen bedoeld om de functionaliteit van een regex in Raku te tonen. Gebruik deze regex niet in deze vorm in productie. |
my $email = 'jan.met@raku.org';
my $regex = / ^ <:L>+ \. <:L>+ \@<:L+:N>+ \. (com|org|net) $ /;
if $email ~~ $regex {
say $/ ~ " lijkt een valide email-adres";
} else {
say "Dit email-adres kon niet gevalideerd worden";
}
jan.met@raku.org lijkt een valide email-adres
-
<:L>accepteert een enkele letter -
<:L>+accepteert één of meer letters -
\.accepteert een enkele [punt] -
\@accepteert een enkel @-karakter -
<:L+:N>accepteert één of meer letters gevolgd door een cijfer -
<:L+:N>+accepteert één of meer letters + cijfer één of meer keer -
(com|org|net)komt overeen met één van de drie alternatieven -
^en$zorgen ervoor dat niets voor of achter het email-adres mogen staan
De regex kan als volgt uit elkaar worden gehaald:
-
voornaam
<:L>+ -
[punt]
\. -
achternaam
<:L>+ -
[bij]
\@ -
bedrijfsnaam
<:L+:N>+ -
[punt]
\. -
com/org/net
(com|org|net)
my $email = 'jan.met@raku.org';
my regex veel-letters { <:L>+ };
my regex punt { \. };
my regex bij { \@ };
my regex veel-letters-cijfers { <:L+:N>+ };
my regex domein { com | org | net }
if $email ~~ / <veel-letters> <punt> <veel-letters> <bij> <veel-letters-nummers> <dot> <domein> / {
say $/ ~ " lijkt een valide email-adres";
} else {
say "Dit email-adres kon niet gevalideerd worden";
}
Een regex op naam wordt als volgt gedefinieerd: my regex regex-naam { regex definitie }
Een regex op naam kan als volgt worden aangeroepen: <regex-naam>
| Zie https://docs.raku.org/language/regexes voor meer informatie over regexen. |
12. Grammatica’s
In het vorige hoofdstuk hebben we een email regex onderverdeeld in meerdere regexen met een naam.
Een grammatica gaat verder op dit idee. Het groupeert regexen met een naam om de vorm van een gehele tekst te beschrijven.
Grammatica’s worden gebruikt om een tekst te ontleden: om te controleren dat het voldoet aan een bepaalde vorm, en om die delen waarin we geïntereseerd zijn er uit te halen.
12.1. Definiëren van een grammatica
Een grammatica wordt gedefinieerd met het sleutelwoord grammar. Regexen met een naam worden gedefinieerd met het sleutelwoord token.
Het token met de naam TOP is special: het beschrijft de vorm van de tekst als een geheel, gebruikmakend van de andere "tokens".
Laten we het email voorbeeld herschrijven als een grammatica.
grammar Email {
token TOP { <voornaam> '.' <achternaam> '@' <bedrijf> '.' <domein> }
token voornaam { <:L>+ }
token achternaam { <:L>+ }
token bedrijf { <:L+:N>+ }
token deomein { com | org | net }
}
TOP definieert dat een email-adres bestaat uit een voornaam, een punt, een achternaam, een apestaartje en een domein.
De andere tokens definiëren hoe elk deel er uit ziet.
De punt en het apenstaartje worden omgeven door aanhalingstekens omdat ze geen alfanumerieke karakters zijn.
12.2. Parsing
A grammar is used by calling its .parse() method on a string.
Een grammatica wordt uitgevoerd door het aanroepen van de .parse() methode
met de string als argument
Scriptgrammar Email {
token TOP { <voornaam> '.' <achternaam> '@' <bedrijf> '.' <domein> }
token voornaam { <:L>+ }
token achternaam { <:L>+ }
token bedrijf { <:L+:N>+ }
token domein { com | org | net }
}
my $email = 'jan.jansen@a2.org';
if Email.parse($email) {
say "$email is een email-adres";
} else {
say "$email is geen geldig email-adres";
}
Uitvoerjohn.doe@route66.org is een email-adres
De aanroep .parse() is alleen succesvol indien de gehele string overeenkomt met TOP. Daarom hebben de ankers ^ en $, die we wél gebruikten in de regex, hier niet nodig.
Vervang nu the email-adres met jan.jansen@a2.xyz en voer heet script weer uit.
Het zal nu "jan.jansen@a2.zyx is geen geldig email-adres" tonen, omdat xyz niet een van de geldige domeinen is.
12.3. Extracting data
When it succeeds, .parse() returns a single match object, which we store in a scalar.
The part of the text that was matched by a token can be accessed using the name of that token, with the same angle brackets that are used with a hash.
Scriptgrammar Email {
token TOP { <voornaam> '.' <achternaam> '@' <bedrijf> '.' <domein> }
token voornaam { <:L>+ }
token achternaam { <:L>+ }
token bedrijf { <:L+:N>+ }
token domein { com | org | net }
}
my $match = Email.parse('jan.jansen@a2.org');
say "Voornaam: " ~ $match<voornaam>;
say "Achternaam:" ~ $match<achternaam>;
say "Bedrijf: " ~ $match<bedrijf>;
say "Domein: " ~ $match<domein>;
UitvoerVoornaam: jan Achternaam: jansen Bedrijf: a2 Domein: org
Met één enkele definitie is een grammatica in staat om zowel het email-adres te valideren, en de gewenste informatie er uit te halen.
| Voor meer informatie over grammatica’s, zie https://docs.raku.org/language/grammars |
13. Raku Modules
Raku is een algemeen toepasbare programmeertaal. Het kan worden gebruikt om vele taken uit te voeren, zoals: manipulatie van tekst, plaatjes, web, databases, netwerkprotocollen, etc.
Herbruikbaarheid is een erg belangrijke eigenschap waardoor programmeurs niet telkens het wiel opnieuw hoeven uit te vinden als ze aan een nieuwe taak beginnen.
Raku maakt het mogelijk om modules aan te maken en die te distribueren. Elke module is een bundel van functionaliteit die kan worden gebruikt zodra deze is geïnstalleerd.
Zef is een programma om modules te beheren dat meestal ook wordt geïnstalleerd.
Om een specifieke module te installeren moet men onderstaand commando in een terminalvenster intypen:
zef install "module name"
| Een overzicht van beschikbare Raku modules is te vinden op: https://raku.land/ |
13.1. Gebruiken van Modules
JSON is a text format for storing and exchanging data.
JSON is een tekst standaard voor het opslaan en uitwisselen van data.
Het wordt gebruikt door vele webdiensten en applicaties, waardoor vroeger of later jouw programma in staat zal moeten zijn om JSON te lezen, of te produceren.
Een JSON document bestaat uit naam/waarde paren en lijsten. Deze komen overeen met hashes en arrays in Raku.
Gelukkig hoef je niet zelf een JSON lezer te ontwikkelenl; er bestaat al een Raku module voor.
Laten we die installeren.
zef install JSON::Fast
En voer dan onderstaand script uit:
use JSON::Fast;
my %gebruiker = naam => 'Jan', leeftijd => 23;
my $json = to-json(%gebruiker);
say $json;
Uitvoer{
"naam": "Jan",
"leeftrijd": 23
}
Om de to-json() functie, die een hash verandert in JSON, uit te kunnen voeren moeten we de benodigde module laden.
Het use sleutelwoord laadt de module, en levert zo een to-json subroutine die kan worden uitgevoerd.
Raku does not keep the keys of a hash in a fixed order, so age might come before name when you run the script. The order can change each time the script runs.
|
Raku houdt de namen in een hash niet in een bepaalde orde, dus leeftijd kan ook vóór naam worden getoond. En die volgorde kan ook verschillend zijn elke keer als dit script wordt uitgevoerd.
|
Dezelfde module levert ook een from-json() subroutine die het omgekeerde doet.
use JSON::Fast;
my $json = '{ "naam": "Jan", "leeftijd": 25 }';
my %gebruiker = from-json($json);
say %gebruiker<naam>;
say %gebruiker<leeftijd>;
UitvoerJan 25
| Voor meer informatie over modules, zie https://docs.raku.org/language/modules |
14. Unicode
Unicode is een standaard voor het coderen en representeren van tekst die de meeste schriften van de wereld ondersteund.
UTF-8 is een manier van het coderen in Unicode van alle mogelijke karakters (ook wel "codepoints" genoemd).
Karakters zijn gedefinieerd door een:
Grafeem: Visuele voorstelling.
Codepoint: Een nummer toegewezen aan dat karakter.
14.1. Het gebruik van Unicode
14.1.1. Specificeren van karakters
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";
Bovenstaande 3 regels laten verschillende manieren zien om een karakter op te bouwen:
-
Door het karakter (grafeem) direct te schrijven
-
Door
\xte gebruiken en het codepoint in hexadecimaal aan te geven -
Door
\cte gebruiken en de naam van het codepoint aan te geven.
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
14.1.2. Combineren van karakters
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";
De letter á kan worden geschreven als:
-
door het unieke codepoint
\x00e1te gebruiken -
door combinatie van codepoints voor
aen acute\x0061\x0301
say "á".NFC;
say "á".NFD;
say "á".uniname;
UitvoerNFC:0x<00e1> NFD:0x<0061 0301> LATIN SMALL LETTER A WITH ACUTE
NFC geeft het unieke codepoint.
NFD haalt het karakter uit elkaar en geeft de codepoints van elk onderdeel.
uniname geeft de naam van de codepoint.
14.1.3. Namen en berekeningen
my $Δ = 1;
$Δ++;
say $Δ;
my $var = 2 + ⅒;
say $var;
14.2. Operaties bekend met Unicode
14.2.1. Nummers
Arabische cijfers zijn de tien cijfers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Deze verzameling van cijfers wordt wereldwijd het meest gebruikt.
In sommige delen van de wereld worden soms andere verzamelingen van cijfers gebruikt.
Je hoeft niets speciaals te doen om cijfers uit deze andere verzamelingen te ondersteunen: alle methodes en operatoren werken zoals te verwachten valt.
say (٤,٥,٦,1,2,3).sort; # (1 2 3 4 5 6)
say 1 + ٩; # 10
14.2.2. Strings
Indien we de algemene string operatoren gebruiken, kan het zijn dat we niet altijd het gewenste resultaat bereiken, met name bij het vergelijken en sorteren.
Vergelijking
say 'a' cmp 'B'; # More
Dit voorbeeld toont that a groter is dan B. De reden hiervoor is dat de numerieke waarde van het "code point" van een onderkast a hoger is dan de numerieke waarde van "code point" van een bovenkast B.
Hoewel dit technisch gezien juist is, is het waarschijnlijk niet waar we op uit waren.
Gelukkig heeft Raku methoden en operator die het Unicode Collation Algorithm implementeren.
Eén daarvan is unicmp dat zich net zo gedraagt als cmp, maar kijkt naar de unicode-eigenschappen.
say 'a' unicmp 'B'; # Less
Zoals je kunt zien geeft het gebruik van de unicmp operator het resultaat dat a kleiner is dan B.
Sorteren
De sort methode sorteert op basis van "code points". Raku geeft je ook een collate methode die sorteert volgens het Unicode Collation Algorithm.
say ('a','b','c','D','E','F').sort; # (D E F a b c)
say ('a','b','c','D','E','F').collate; # (a b c D E F)
| Voor meer informatie over Unicode, zie https://docs.raku.org/language/unicode |
15. Parallelisme, Gelijktijdigheid en Asynchroniciteit
Raku has built-in features that let a program do several things at the same time.
In this chapter we will take a look at how to run code in parallel, and how to deal with tasks that take time to complete.
Raku heeft een aantal functies ingebouwd waarmee een programma een aantal dingen tegelijkertijd kan uitvoeren.
In dit hoofdstuk zullen we kijken hoe je code gelijktijdig kan laten uitvoeren, en hoe om te gaan met taken die langere tijd nodig hebben om tot een goed einde te komen.
15.1. Parallelisme
Onder normale omstandigheden worden alle taken in een programma achter elkaar uitgevoerd.
Dit hoeft niet noodzakelijkerwijs een probleem te zijn tenzij wat je probeert te doen een hoop tijd vergt.
Gelukkig heeft Raku een aantal mogelijkheden die het mogelijk maken om delen van een programma parallel uit te laten voeren.
Op dit moment is het belangrijk om op te merken dat parallelisme twee dingen kan betekenen:
-
Taak Parallelisme: Twee (of meer) onafhankelijke expressies die parallel worden uitgevoerd..
-
Data Parallelisme: Een enkele expressie die over een lijst van elementen parallel wordt uitgevoerd.
Laten we beginnen met de laatste.
15.1.1. Data Parallelisme
my @array = 0..50000; # vullen van het Array
my @resultaat = @array.map({ is-prime $_ }); # roep is-prime aan op elk element van het Array
say now - INIT now; # laat de tijd zien die het script nodig had om te voltooien
We doen maar een enkele opdracht: @array.map({ is-prime $_ })
De is-prime subroutine wordt voor elk element van het array achter elkaar aangeroepen:
is-prime @array[0] en dan is-prime @array[1] en dan is-prime @array[2] etc.
is-prime tegelijkertijd op meer dan één array element aanroepen:my @array = 0..50000; # vullen van het Array
my @resultaat = @array.race.map({ is-prime $_ }); # roep is-prime aan op elk element van het Array
say now - INIT now; # laat de tijd zien die het script nodig had om te voltooien
Merk op dat we race in deze expressie hebben gebruikt.
Deze methode maakt het mogelijk om in parallel over de array elementen te gaan.
Nadat je beide voorbeelden (met en zonder race) hebt uitgevoerd, vergelijk dan de tijd die nodig was om de scripts te laten voltooien.
|
race
hyper
Als je beide voorbeelden uitvoert, zul je zien dat de ene wel gesorteerd is en de andere niet. |
15.1.2. Taak Parallelisme
my @array1 = (0..49999);
my @array2 = (2..50001);
my @resultaat1 = @array1.map( {is-prime($_ + 1)} );
my @resultaat2 = @array2.map( {is-prime($_ - 1)} );
say @resultaat1 eqv @resultaat2;
say now - INIT now;
-
We definiëren 2 arrays
-
we voeren dezelfde operatie uit op beide arrays en slaan het resultaat op
-
en kijken of beide resultaten hetzelfde zijn.
Het script wacht totdat @array1.map( {is-prime($_ + 1)} ) klaar is
en gaat dan @array2.map( {is-prime($_ - 1)} ) uitvoeren.
De operaties die we op elk array uitvoeren zijn niet afhankelijk van elkaar.
my @array1 = (0..49999);
my @array2 = (2..50001);
my $belofte1 = start @array1.map( {is-prime($_ + 1)} );
my $belofte2 = start @array2.map( {is-prime($_ - 1)} );
my @resultaat1 = await $belofte1;
my @resultaat2 = await $belofte2;
say @resultaat1 eqv @resultaat2;
say now - INIT now;
De start subroutine gaat de gegeven code in parallel uitvoeren maar geeft eerst een Promise (belofte) object terug, of kortweg een belofte.
Als de code kan worden uitgevoerd zonder problemen, dan wordt de belofte gehouden (kept).
Als de code een exception werpt, dan zal de belofte worden gebroken (broken).
De await subroutine wacht op een belofte.
Als de belofte gehouden wordt, dan geeft het de geproduceerde waarden terug.
Als de belofte is gebroken dan zal het de exception (opnieuw) werpen.
Controleer de tijd die nodig was om elk script uit te voeren.
|
Parallelisme voegt altijd extra overhead toe. Als die overhead relatief groot is, zal een script trager zijn dan de niet parallele versie. Dat is de reden waarom het gebruik van race, hyper en start in tamelijk simpele scripts een reden van vertraging kan zijn.
|
15.2. Gelijktijdigheid en Asynchroniciteit
In the previous section, we used parallelism to make a computation faster.
Gelijktijdigheid en Asynchroniciteit zijn antwoorden op verschillende problemen. Een programma moet vaak wachten: op een wekkertje, op een bestand, of op een antwoord van het netwerk.
In plaats van niets te doen tijdens het wachten kan een programma iets anders doen en pas later het resultaat behandelen zodra het beschikbaar is.
15.2.1. Beloften
We hebben al eerder beloften gezien met start en await.
Een belofte staat voor een resultaat dat nog niet beschikbaar is, maar wel op een gegeven moment beschikbaar zal zijn.
Scriptmy $koffie = start { sleep 2; "Koffie is gezet" };
my $toast = start { sleep 1; "Toast is klaar" };
say "Ontbijt klaarzetten";
say await $toast;
say await $coffee;
UitvoerOntbijt klaarzetten Toast is klaar Koffie is gezet
sleep pauzeert voor het gegeven aantal seconden. We gebruiken het hier om een taak te simuleren die tijd vergt.
start meldt een taak aan en geeft direct een belofte terug. Het wacht niet tot the taak voltooid is.
Dat is waarom Ontbijt klaarzetten eerst wordt getoond.
await wacht tot de belofte gehouden wordt, en geeft dan de waarde terug.
Beide taken lopen gelijktijdig, zodat het script maar 2 seconden duurt, in plaats van 3.
15.2.2. Aanvoeren
A promise delivers a single result. A supply delivers many values, one after the other, over a period of time. Een belofte geeft een enkel resultaat terug. Een aanvoer (supply) levert meerdere waarden, één voor één, gedurende een periode.
Supply.interval(1) is een aanvoer die eklek seconde een nieuwe waarde levert: 0, 1, 2 enzovoort.
Om te reageren op elke waarde die wordt geleverd gebruiken we react en whenever.
Scriptreact {
whenever Supply.interval(1) -> $seconde {
say "Tik $seconde";
done if $seconde == 3;
}
}
say "Klaar";
UitvoerTik 0 Tik 1 Tik 2 Tik 3 Klaar
whenever voert het bijbehorende blok uit elke keer als een waarde wordt ontvangen.
react zorgt ervoor dat het script blijft wachten totdat done wordt aangeroepen.
done if $seconde == 3 stopped het react blok na de vierde waarde. Zonder die conditie zou het script nooit eindigen.
Een react blok kan meer dan één whenever bevatten. Dit laat het toe dat een script meerdere dingen tegelijk kan doen.
Scriptreact {
whenever Supply.interval(1) -> $seconde {
say "Tick $seconde";
}
whenever Promise.in(3.5) {
say "De tijd is om";
done;
}
}
UitvoerTik 0 Tik 1 Tik 2 Tik 3 De tijd is om
Promise.in(3.5) geeft een belofte die gehouden wordt na 3.5 seconden.
De eeste whenever laat elke second een tik zien.
De tweede whenever wacht tot de belofte gehouden worden, en beëindigt dan het react blok.
| Zie https://docs.raku.org/language/concurrency voor meer informatie over asynchroon programmeren in Raku |
16. Aanroepen van C-bibliotheken
Raku geeft je de mogelijkheid om C-bibliotheken in je programma te gebruiken door middel van de "Native Calling Interface".
NativeCall is a standaard module die altijd met Raku wordt meegeleverd en die je een hoop functionaliteit biedt die het gemakkelijker maakt om programma’s die in C geschreven zijn met programma’s die in Raku geschreven zijn, samen te laten werken.
16.1. Aanroepen van een functie
16.1.1. The C bibliotheek
Bekijk hier het C-programma dat een functie definieert met de naam hallovanc.
Deze functie drukt de string Hallo van C af op het scherm. Het ontvangt geen parameters, en het geeft ook geen enkele waarde terug.
#include <stdio.h>
void hallovanc () {
printf("Hallo van C\n");
}
16.1.2. Compileren van de bibliotheek
Afhankelijk van het Operating System dat je gebruikt, moet je de volgende commando’s uitvoeren om een bibliotheek te maken van bovenstaand C programma.
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
16.1.3. Het Raku script
Maak vervolgens een bestand aan met het volgende Raku programma, in dezelfde directory waar je de C-bibliotheek hebt gecompileerd. En voer dat programma dan uit.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hallovanc() is native(LIBPATH) { * }
hallovanc();
Ten eerste gaven we aan dat we de NativeCall module wilden gebruiken.
Vervolgens maakten we een constante LIBPATH aan waarin de locatie van de C-bibliotheek staat.
Merk op dat $*CWD de huidige directory aangeeft.
Vervolgens maakten we een nieuwe Raku subroutine hallovanc die als een verpakking functioneert van de functie met dezelfde naam in de C-bibliotheek die aangegeven wordt door LIBPATH.
Dit alles wordt gedaan door het gebruik van de is native eigenschap.
Tot slot roepen we de Raku subroutine aan.
Uiteindelijk komt het er op neer dat we een subroutine hebben gedefinieerd met de is native eigenschap, en het aangeven van de naam van de C-bibliotheek.
16.2. Andere naam geven aan de functie
Zojuist hebben we gezien dat we een simpele C-functie kunnen aanroepen door deze te verpakken in een Raku subroutine met dezelfde naam door het gebruik van de is native eigenschap.
In sommige gevallen willen we dat name van de Raku subroutine anders is.
Dat kun je doen door het gebruik van de is symbol eigenschap.
Laten we bovenstaand Raku programma aanpassen en de Raku subroutine de naam hallo geven in plaats van hallovanc.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hallo() is native(LIBPATH) is symbol('hallovanc') { * }
hallo();
In de situatie waarin de Raku subroutine een andere naam heeft als de naam van de functie aan de C-kant, kun je de is symbol eigenschap gebruiken met de naam van de C-functie.
16.3. Doorgeven van argumenten
Compileer deze aangepaste C-bibliotheek en voer dan het onderstaande Raku programma uit.
Merk op dat we zowel de C-bibliotheek als het Raku programma zo hebben aangepast dat ze beide een string verwachten (char* in C, en Str in Raku).
#include <stdio.h>
void hallovanc (char* naam) {
printf("Hallo, %s! Dit is C!\n", naam);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hallo(Str) is native(LIBPATH) is symbol('hallovanc') { * }
hallo('Jane');
16.4. Teruggeven van waarden
Laten we dit nog eens doen zodat het resultaat wordt teruggegeven van een simpele berekening die twee gehele getallen optelt.
Compileer de C-bibliotheek en voer het Raku programma uit.
int telop (int a, int b) {
return (a + b);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub telop(int32,int32 --> int32) is native(LIBPATH) { * }
say telop(2,3);
Merk op dat zowel de C-functie als de Raku subroutine nu 2 gehele getallen verwachten (int in C, en int32 in Raku).
16.5. Types
Je zult je wellicht hebben afgevraagd waarom we int32 hebben gebruikt in plaats van Int in dit laatste voorbeeld.
Sommige Raku typen, zoals Int, Rat, etc., kunnen niet worden gebruikt om waarden naar een C-functie te sturen, of waarden van een C-functie terug te ontvangen.
Men moet in Raku dezelfde typen gebruiken als die in C worden gebruikt.
Gelukkigerwijze bestaan er vele types in Raku die overeenkomen met types zoals die in C worden gebruikt.
| C Type | Raku Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arrays: Bijvoorbeeld |
|
| Voor meer informatie over de Native Calling Interface, zie https://docs.raku.org/language/nativecall |
17. De Community
-
#raku IRC kanaal. Veel discussies vinden plaats op IRC. Dit zou voor jou het vaste aanspreekpunt moeten zijn voor elke vraag waarop je direct antwoord wilt.
-
StackOverflow Raku-vragen is een plaats waar vragen over Raku meer diepgaand beantwoord worden.
-
Raku Weekly een wekelijks overzicht van veranderingen in en rond Raku
-
Planet Raku is een blog aggregator. Hier kun je blog posts over Raku lezen
-
/r/rakulang ook op Reddit kun je over Raku lezen
-
Raku Foundation de stichting voor de Raku Programming Language