Dieser Text soll einen schnellen Überblick über die Programmiersprache Perl 6 bieten. Perl 6-Neulingen soll er beim schnellen Einstieg helfen.
Manche Abschnitte dieses Texts referenzieren weitere (auführlichere/vollständige und genauere) Teile der Perl 6 Dokumentation (vorerst zumeist in englischer Sprache). Diese sollten zur Vertiefung speziellerer Themen gelesen werden.
In diesem Text werden immer wieder Beispiele für die meisten Themen gezeigt. Nehmen Sie sich die Zeit, alle Beispiele zu üben, um sie besser zu verstehen.
Dieser Text steht unter der Creative Commons Attribution-ShareAlike 4.0 International License.
Diese Lizenz können Sie hier ansehen oder kopieren:
Wollen Sie an diesem Text mitarbeiten, dann geht das hier:
Jeder Feedback ist erwünscht:
-
naoum@hankache.com - Englisch
-
perl@myobj.com - Deutsch
1. Einführung
1.1. Was ist Perl 6?
Perl 6 ist eine universelle stufenweise typisierte Hochsprache. Perl 6 ist multi-paradigmatisch. Es unterstützt prozedurales, objektorientiertes und funktionales Programmieren.
-
TMTOWTDI (Sprich: Tim Toady): There is more than one way to do it. (Es gibt mehrere Wege zum Ziel)
-
Easy things should stay easy, hard things should get easier, und impossible things should get hard. (Einfaches soll einfach bleiben, schweres einfacher werden, und unmögliches lediglich schwierig sein.)
1.2. Jargon
-
Perl 6: Eine Sprachenspezifikation mit einer Testsuite. Implementierungen, die diese Tests bestehen nennt man Perl 6.
-
Rakudo: Ein Compiler für Perl 6.
-
Rakudobrew: Ein Installationenmanager für Rakudo.
-
Zef: Ein Perl 6 Moduleinstallierer.
-
Rakudo Star: Ein Paket mit Rakudo, Zef, einer Sammlung von Perl 6 Modulen und Dokumentation.
1.3. Perl 6 installieren
Um Rakudo-Star zu installieren, führe diese Kommandos in einem Terminalfenster aus:
mkdir ~/rakudo && cd $_
curl -LJO https://rakudo.org/latest/star/src
tar -xzf rakudo-star-*.tar.gz
mv rakudo-star-*/* .
rm -fr rakudo-star-*
./bin/rstar install
echo "export PATH=$(pwd)/bin/:$(pwd)/share/perl6/site/bin:$(pwd)/share/perl6/vendor/bin:$(pwd)/share/perl6/core/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc
Mehr Informationen stehen unter: https://rakudo.org/star/source
Folgen Sie der Anleitung für Linux
ODER
Mit homebrew installieren: brew install rakudo-star
-
Den neusten Installer downloaden (Datei mit der Extension .MSI) auf http://rakudo.org/downloads/star/
Bei einem 32-Bit-System, die x86-Datei und bei einem 64-Bit-System die x86_64-Datei verwenden. -
Nach der Installation C:\rakudo\bin dem PATH hinzufügen.
-
Das offizielle Docker Image beziehen:
docker pull rakudo-star
-
Dann einen Container mit dem Image starten
docker run -it rakudo-star
1.4. Perl 6 Code ausführen
Perl 6 Code kann mit dem REPL (Read-Eval-Print Loop) ausgeführt werden. Dazu in ein Terminalfenster perl6
eingeben, Enter] drücken. Darauf hin erscheint ein >
Prompt.
Nun kann eine Zeile Perl 6 Code eingegeben und wieder [Enter] gedrückt werden.
Der REPL gibt das Ergebnis der Zeile aus. Nun kann eine weitere Zeile Code eingegeben und wieder [Enter] gedrückt werden. Um REPL zu verlassen, kann exit
eingegeben und [Enter] gedrückt werden.
Alternativ den Code in eine Datei schreiben, abspeichern und ausführen.
Es wird empfohlen, Perl 6-Dateien mit der Dateierweiterung .pl6
zu versehen.
Im Terminal kann nun perl6 filename.pl6
eingegeben und [Enter] gedrückt werden, um den Code auszuführen. Anders als in REPL werden nun nicht automatisch die Ergebnisse jeder Zeile ausgegeben. Um Ausgaben auf dem Terminal zu zeigen, müßte der Code nun zum Beispiel eine Anweisung wie etwa say
enthalten.
REPL wird meistens dafür verwendet, eine bestimmte Codestelle auszuprobieren, etwa eine einzelne Zeile.
Programme mit mehr als einer Zeile sollten besser in eine eigene Datei geschrieben werden, aus der sie dann ausgeführt werden.
Einzelne Zeilen können auch nicht-interaktiv auf der Kommandozeilte getestet werden, indem perl6 -e 'Code steht hier'
eingegeben und [Enter] gedrückt wird.
Rakudo Star beinhaltet einen Zeileineditor der dabei hilft, REPL leichter verwendbar zu machen. Ist nur das einfache Rakudo aber nicht Rakudo Star installiert, dann sind wahrscheinlich keine Zeileneditorfunktionen aktiviert (Auf- und Abwärtspfeile für die Kommandozeilenhistorie, Links- und Rechtspfeil um die Eingabe zu bearbeite, Tabulator zur Vervollständigung). Das läßt sich mit einem der folgenden Kommandos nachholen:
|
1.5. Editoren
Da die meisten Perl-6-Programme in Dateien geschrieben werden, sollte ein guter Text-Editor verwendet werden, der Perl-6-Syntax erkennt.
Selbst verwende und empfehle ich Atom: ein moderner Text-Editorder das Perl 6 Syntaxhighlighting gleich mitbringt. Perl6-fe ist ein alternativer Perl 6 Syntax-Highlighter für Atom, der aus dem Originalpaket stammt und dann vielfach erweitert und korrigiert wurde.
Neuere Vim-Versionen haben das Syntax-Highlighting schon inklusive. Emacs und Padre benötigen die Installation von zusätzlichen Packeten.
1.6. Hello World!
Wir fangen mit dem hello world
Ritual an.
say 'hello, world';
kann auch so geschrieben werden:
'hello, world'.say;
1.7. Die Syntax
Perl 6 unterstützt die sogenannte freie Form/free form: Meistens kann beliebig viel whitespace verwendet werden.
Ein Ausdruch (Statement) ist typischerweise eine logische Code-Zeile, die mit einem Semikolon abgeschlossen wird:
say "Hallo" if True;
Eine Expression ist eine Sonderform des Statements, die einen Wert zurückgibt:
1+2
will return 3
Expressions bestehen aus Terms und Operatoren.
Terms sind:
-
Variablen: Ein Wert, der bearbeitet/verändert werden kann.
-
Literals: Eine Konstante, etwa eine Zahl oder ein String.
Operatoren werden in Typen klassifiziert:
Typ |
Erklärung |
Beispiel |
Prefix |
Vor dem Term. |
|
Infix |
Zwischen Terms |
|
Postfix |
Nach dem Term |
|
Circumfix |
Um den Term (herum) |
|
Postcircumfix |
Nach dem Term und um einen weiteren Term (herum) |
|
1.7.1. Identifier
Identifier sind Namen, die Terms gegeben werden, wenn sie definiert werden.
-
Sie müssen mit einem Buchstaben des Alphabets oder einem Underscore anfangen.
-
Sie dürfen Ziffern beinhalten, jedoch nicht als erstes Zeichen.
-
Sie können Bindestriche und Apostrophen enthalten, jedoch weder als erstes noch als letztes Zeichen, und vorraussgesetzt, rechts von jedem Bindestrich bzw. Apostrophen steht ein Buchstabe.
Gültig |
Ungültig |
|
|
|
|
|
|
|
|
|
|
-
Kamel-/Camel case:
variableNo1
-
Kebab case:
variable-no1
-
Schlangen-/Snake case:
variable_no1
Es steht jedem frei, welcher Namenskonvention man nun folgen möchte. Als guter Stil gilt es, konsistent bei einer davon zu bleiben.
Die Verwendung sinnvoller Namen vereinfacht sowohl Dir als auch anderen das Programmiererleben.
-
var1 = var2 * var3
ist syntaktisch korrekt, jedoch wird der Sinn nicht deutlich. -
monatlicher-lohn = tages-rate * arbeitstage
wäre ein deutlicheres Vorgehen.
1.7.2. Kommentare
Ein Kommentar ist Text, der vom Kompiler ignoriert wird, und für Notizen verwendet wird.
Es gibt drei Sorten von Kommentaren:
-
Einzelne Zeile:
# Dies ist ein Einzelzeilen-Kommentar
-
Eingebettet/embedded:
say #`(Dies ist ein eingebetteter Kommentar) "Hello, world."
-
Mehrzeilig/multi line:
=begin comment Dies ist ein mehrzeiliger Kommentar. Kommentar 1 Kommentar 2 =end comment
1.7.3. Anführungszeichen (quotes)
Strings müssen durch entweder doppelte oder einfache Anführungszeichen eingerahmt werden.
Nutze immer doppelte Anführungszeichen:
-
wenn der String einen Apostrophen enthält.
-
wenn der String eine Variable enthält, die interpoliert werden soll.
say 'Hello World'; # Hello World
say "Hello World"; # Hello World
say "Don't"; # Don't
my $name = 'Hans Meier';
say 'Hallo $name'; # Hallo $name
say "Hallo $name"; # Hallo Hans Meier
2. Operatoren
Diese Tabelle zeigt die am Häufigsten verwendten Operatoren.
Operator | Typ | Beschreibung | Beispil | Resultat |
---|---|---|---|---|
|
|
Addition |
|
|
|
|
Subtraktion |
|
|
|
|
Multiplikation |
|
|
|
|
Potenz |
|
|
|
|
Division |
|
|
|
|
Ganzzahlige (integer) Division (abgerundet) |
|
|
|
|
Modulo |
|
|
|
|
Teilbarkeit |
|
|
|
|
|||
|
|
Größter gemeinsamer Nenner |
|
|
|
|
Kleinstes gemeinsames Produkt |
|
|
|
|
Gleich |
|
|
|
|
Ungleich |
|
|
|
|
Kleiner als |
|
|
|
|
Größer als |
|
|
|
|
Kleiner als oder gleich |
|
|
|
|
Größer als oder gleich |
|
|
|
|
String Gleichheit |
|
|
|
|
String Ungleichheit |
|
|
|
|
Zuweisung |
|
|
|
|
String Verkettung |
|
|
|
|
|||
|
|
String Wiederholung |
|
|
|
|
|||
|
|
Smart match |
||
|
|
Inkrementierung |
|
|
|
Inkrementierung |
|
|
|
|
|
Dekrementierung |
|
|
|
Dekrementierung |
|
|
|
|
|
Verwandle den Operanden in einen numerischen Wert |
|
|
|
|
|||
|
|
|||
|
|
Verwandle den Operanden in einen numerischen Wert und gib seine Negation zurück |
|
|
|
|
|||
|
|
|||
|
|
Verwandle den Operanden in einen booleschen Wert |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Verwandle den Operanden in einen booleschen Wert und gib seine Negation zurück |
|
|
|
|
Bereich (range) Konstruktor |
|
|
|
|
Bereich (range) Konstruktor |
|
|
|
|
Bereich (range) Konstruktor |
|
|
|
|
Bereich (range) Konstruktor |
|
|
|
|
Bereich (range) Konstruktor |
|
|
|
|
Lazy Listen Konstruktor |
|
|
|
|
Flattening |
|
|
|
|
Für die vollständige Liste der Operatoren gibt es eine eigene Dokumentationsseite, die auch ihre Präzedenz auflistet: http://doc.perl6.org/language/operators |
3. Variablen
Perl 6 Variables werden in drei Kategorien gegliedert: Scalare, Arrays und Hashs.
Eine Sigille (Signum in Latein) ist a Zeichen das als Prefix Variablen kategorisiert.
-
$
wird für Skalare verwendet -
@
wird für Arrays verwendet -
%
wird für Hashs verwendet
3.1. Skalar
Ein Skalar enthält einen Wert oder eine Referenz.
#String
my $name = 'Hans Meier';
say $name;
#Integer
my $age = 99;
say $age;
Allerlei spezielle Operationen können an einem Skalar ausgeführt werden, abhängig von dem Wert, den er enthält.
my $name = 'Hans Meier';
say $name.uc;
say $name.chars;
say $name.flip;
Hans Meier
10
reieM snaH
Für die vollständige Liste der Methoden, die an Strings ausgeführt werden können, siehe http://doc.perl6.org/type/Str |
my $alter = 17;
say $alter.is-prime;
True
Für die vollständige Liste der Methoden, die an Integern ausgeführt werden können, siehe http://doc.perl6.org/type/Int |
my $alter = 2.3;
say $alter.numerator;
say $alter.denominator;
say $alter.nude;
23
10
(23 10)
Für die vollständige Liste der Methoden, die an Rationalen Zahlen ausgeführt werden können, siehe http://doc.perl6.org/type/Rat |
3.2. Array
Ein Array ist eine variable Liste, die mehrere Werte enthalten kann.
my @tiere = 'Kamel','Lama','Eule';
say @tiere;
Viele Operationen sind an Array möglich, wie man in der Tabelle unten erkennen kann:
Die Tilde ~ wird verwendet um Strings zu verketten.
|
Script
my @tiere = 'Kamel','Vikunja','Lama';
say "Im Zoo gibt es " ~ @tiere.elems ~ " Tiere";
say "Die Tiere sind: " ~ @tiere;
say "Ich werde eine Eule für den Zoo adoptieren";
@tiere.push("Eule");
say "Nun gibt es im Zoo: " ~ @tiere;
say "Das erste Tier, das wir adoptierten war das " ~ @tiere[0];
@tiere.pop;
say "Leider ist die Eule davongeflogen und nun sind nur noch: " ~ @tiere;
say "Wir schließen den Zoo und behalten nur noch ein Tier";
say "Wir trennen uns von: " ~ @tiere.splice(1,2) ~ " und behalten das " ~ @tiere;
Ausgabe
Im Zoo gibt es 3 Tiere
Die Tiere sind: Kamel Vikunja Lama
Ich werde eine Eule für den Zoo adoptieren
Nun gibt es im Zoo: Kamel Vikunja Lama Eule
Das erste Tier, das wir adoptierten war das Kamel
Leider ist die Eule davongeflogen und nun sind nur noch: Kamel Vikunja Lama
Wir schließen den Zoo und behalten nur noch ein Tier
Wir trennen uns von: Vikunja Lama und behalten das Kamel
.elems
gibt die Anzahl der Elemente in einem Array zurück.
.push()
fügt ein Element an das Array an.
Ein Element kann über seine Position im Array abgerufen werden: @tiere[0]
.
.pop
entfernt das letzte Element aus dem Array.
.splice(a,b)
entfernt b
Elemente ab Position a
.
3.2.1. Arrays mit fester Elementanzahl (fixed-size)
Ein einfaches Array wird wie folgt deklariert:
my @array;
Ein einfaches Array kann unbegrenzte Länge haben, das nennt man Selbsterweiternd (auto-extending).
Das Array nimmt beliebig viele Variablen an, es gibt keine Einschränkung.
Im Gegensatz dazu lassen sich auch Arrays mit festgelegter Elementanzahl erstellen.
Diese können nicht mehr über die definierte Anzahl der Elemente hinaus erweitert werden.
Um ein solches Array zu deklarieren, wird die maximale Anzahl der Elemente in eckigen Klammern direkt nach seinem Namen angegeben:
my @array[3];
Dieses Array kann nun bis zu 3 Werte enhalten, mit den Indexzahlen von 0 bis 2.
my @array[3];
@array[0] = "erster Wert";
@array[1] = "zweiter Wert";
@array[2] = "dritter Wert";
Es wird nicht gelingen, diesem Array einen vierten Wert zuzuweisen:
my @array[3];
@array[0] = "erster Wert";
@array[1] = "zweiter Wert";
@array[2] = "dritter Wert";
@array[3] = "vierter Wert";
Index 3 for dimension 1 out of range (must be 0..2)
3.2.2. Multidimensionale Arrays
Die bisher verwendeten Arrays waren eindimensional.
Wir können aber auch multidimensionale Arrays in Perl 6 definieren.
my @tbl[3;2];
Dieser Array ist zweidimensional. Die erste Dimension kann bis zu 3 Werte und die zweite Dimension bis zu 2 Werte enthalten.
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
[[1 x] [2 y] [3 z]]
Für die vollständige Referenz zu Arrays, siehe http://doc.perl6.org/type/Array |
3.3. Hashs
my %hauptstadt = ('Großbritannien','London','Deutschland','Berlin');
say %hauptstadt;
my %hauptstadt = (Großbritannien => 'London', Deutschland => 'Berlin');
say %hauptstadt;
Einige der Methoden, die auf Hashs angewendet werden können sind:
Script
my %hauptstadt = (Großbritannien => 'London', Deutschland => 'Berlin');
%hauptstadt.push: (Frankreich => 'Paris');
say %hauptstadt.kv;
say %hauptstadt.keys;
say %hauptstadt.values;
say "Die Hauptstadt von Frankreich ist: " ~ %hauptstadt<Frankreich>;
Ausgabe
(Deutschland Berlin Frankreich Paris Großbritannien London)
(Deutschland Frankreich Großbritannien)
(Berlin Paris London)
Die Hauptstadt von Frankreich ist: Paris
.push: (key => 'Value')
fügt ein weiteres Schlüssel-Wert-Paar (key/value pair) hinzu.
.kv
gibt eine Liste mit allen Schlüsseln und Werten aus.
.keys
gibt eine Liste mit allen Schlüsseln aus.
.values
gibt eine Liste mit allen Werten aus.
Einzelne Werte im Hash können wir über den Schlüssel wie folgt adressieren %hash<key>
Für die vollständige Referenz zu Hashs, siehe http://doc.perl6.org/type/Hash |
3.4. Typen
In den bisherigen Beispielen haben wir nicht erwähnt, welche Typen die Variablen beinhalten sollten.
.WHAT gibt den Typ des Wertes einer Variable zurück.
|
my $var = 'Text';
say $var;
say $var.WHAT;
$var = 123;
say $var;
say $var.WHAT;
Im obigen Beispiel zu sehen war der Typ des Wertes der Variable $var
zuerst (Str) und dann (Int).
Dieser Programmierstil wird dynamisches Typisieren (dynamic typing) genannt. Dynamisch in dem Sinne, dass Variablen beliebige Typen enthalten können.
Nun probieren wir ein weiteres Beispiel:
Wichtig ist das Int
vor dem Namen der Variable.
my Int $var = 'Text';
say $var;
say $var.WHAT;
Das schlägt fehl und gibt diese Fehlermeldung aus: Type check failed in assignment to $var; expected Int but got Str
Was ist passiert? Zuvor wurde bestimmt, dass die Variable vom Typ (Int) sein sollte. Als versucht wurde, einen Wert vom Typ (Str) zuzuweisen ging schlug der Vorgang fehl.
Dieser Programmierstil wird statisches Typisieren (static typing) genannt. Statisch da alle Variablentypen definiert sind bevor ihnen Werte zugewiesen werden, und die Typen sich nicht ändern können.
Perl 6 ist eine stufenweise typisierte Sprache gradually typed; es erlaubt sowohl statisches als auch dynamisches Typisieren.
my Int @array = 1,2,3;
say @array;
say @array.WHAT;
my Str @mehrsprachig = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは";
say @mehrsprachig;
say @mehrsprachig.WHAT;
my Str %hauptstädte = (Großbritannien => 'London', Deutschland => 'Berlin');
say %hauptstädte;
say %hauptstädte.WHAT;
my Int %ländervorwahlen = (Großbritannien => 44, Deutschland => 49);
say %ländervorwahlen;
say %ländervorwahlen.WHAT;
Nun folgt eine Liste der am häufigsten verwendeten Typen.
Wahrscheinlich werden Sie die ersten beiden nie verwenden, aber zur Information sind sie mit aufgeführt.
|
|
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.5. Introspektion
Introspektion ist der Vorgang der Informationsabfrage über die Eigenschaften eines Objekts, wie zum Beispiel sein Typ.
In einem vorherigen Beispiel haben wie .WHAT
verwendet umd den Typ des Wertes einer Variable abzufragen.
my Int $var;
say $var.WHAT; # (Int)
my $var2;
say $var2.WHAT; # (Any)
$var2 = 1;
say $var2.WHAT; # (Int)
$var2 = "Hallo";
say $var2.WHAT; # (Str)
$var2 = True;
say $var2.WHAT; # (Bool)
$var2 = Nil;
say $var2.WHAT; # (Any)
Der Typ einer Variable mit einem zugewiesenen Wert korreliert mit ihrem Wert.
Der Typ einer stark deklarierten leeren Variable ist vom Typ mit dem sie deklariert wurde.
Der Typ einer leeren Variable, die nichtstark deklariert wurde ist (Any)
Um den Wert einer Variable zu löschen, weist man ihr Nil
zu.
3.6. Gültigkeitsbereich (scope)
Bevore eine Variable zum erstenmal verwendet wird, muss sie deklariert werden.
Mehrere Deklaratoren werden in Perl 6 verwendet, in den Beispielen wurde dafür bisher my
verwendet.
my $var=1;
Der Deklarator my
verleiht der Variable lexikalischen Gültigkeitsbereich.
Das heißt, die Variable ist nur innerhalb des Blocks verwendbar, in dem sie deklariert wurde.
Ein Block in Perl 6 wird durch { }
begrenzt.
Wenn noch kein Block da ist, ist der Gültigkeitsbereich der Variable das ganze Perl Skript.
{
my Str $var = 'Text';
say $var; #ist verfügbar
}
say $var; #ist nicht verfügbar, gibt einen Fehler zurück
Da eine lexikalische Variable nur in dem Block verfügbar ist, in dem sie definiert wurde, kann die selbe Variable in einem weitern Block erneut definiert werden.
{
my Str $var = 'Text';
say $var;
}
my Int $var = 123;
say $var;
3.7. Zuweisung im Gegensatz zu Bindung
Die bisherigen Beispiele haben gezeigt, wie Variablen Werte zugewiesen werden.
Zuweisung geschieht mit dem =
Operator.
my Int $var = 123;
say $var;
Wir können den Wert, der einer Variablen zugewiesen wurde ändern:
my Int $var = 123;
say $var;
$var = 999;
say $var;
Ausgabe
123
999
Andererseits können wie einen Wert, der an eine Variable gebunden wurde nicht ändern.
Bindung geschieht mit dem :=
Operator.
my Int $var := 123;
say $var;
$var = 999;
say $var;
Ausgabe
123
Cannot assign to an immutable value
my $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Ausgabe
7
8
Variablenbindungen funktionieren in beide Richtungen, wie man hier erkennen kann.
$a := $b
und $b := $a
bewirken das Gleiche.
Für mehr Information über Variablen, siehe http://doc.perl6.org/language/variables |
4. Funktionen und Mutatoren
Der Unterschied zwischen Funktionen und Mutatoren ist wichtig.
Funktionen ändern den Ursprungszustand des Objekts, auf das man sie anwendet, nicht.
Mutatoren ändern den Zustand des Objekts.
Skript
1
2
3
4
5
6
7
8
9
10
my @zahlen = [7,2,4,9,11,3];
@zahlen.push(99);
say @zahlen; #1
say @zahlen.sort; #2
say @zahlen; #3
@zahlen.=sort;
say @zahlen; #4
Ausgabe
[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
ist ein Mutator, es ändert den Zustand des Arrays (#1)
.sort
ist eine Funktion, es gibt ein sortiertes Array aus, verändert aber nicht den Zustand des Arrays, auf das es angewandt wurde:
-
(#2) zeigt, dass ein sortiertes Array ausgegeben wird.
-
(#3) zeigt, dass das ursprüngliche Array unverändert geblieben ist.
Um eine Funktion als Mutator zu verwenden, wird .=
anstelle von .
benutzt (#4) (Zeile 9 im Skript)
5. Schleifen (loops) und Bedingungen (conditions)
Perl 6 hat viele Bedingungs- und Schleifenkonstrukte.
5.1. if
Der Code wird nur ausgeführt, wenn die Bedingung zutrifft.
my $alter = 19;
if $alter > 18 {
say 'Willkommen'
}
In Perl 6 können Code und Bedingung auch in umgekehrter Reihenfolge geschrieben werden.
Auch wenn der Code zuerst geschrieben wird, wird immer noch die Bedingung zuerst ausgewertet.
my $alter = 19;
say 'Willkommen' if $alter > 18;
Falls die Bedingung nicht erfüllt wird, können alternative Blöcke ausgeführt werden, und zwar mit:
-
else
-
elsif
#führe diesen Code für verschiedene Werte der Variable aus
my $anzahl-sitze = 9;
if $anzahl-sitze <= 5 {
say 'Es ist ein PKW'
} elsif $anzahl-sitze <= 7 {
say 'Es ist ein 7-Sitzer'
} else {
say 'Es ist ein Bus'
}
5.2. unless
Die negierte Form einer if-Bedingung kann als unless
geschrieben werden.
Dieser Code:
my $saubere-schuhe = False;
if not $saubere-schuhe {
say 'Schuhe Putzen ist angesagt'
}
kann auch so geschrieben werden:
my $saubere-schuhe = False;
unless $saubere-schuhe {
say 'Schuhe Putzen ist angesagt'
}
Negation in Perl 6 wird entweder mit !
oder mit not
erreicht.
unless (condition)
wird anstelle von if not (condition)
verwendet.
unless
kann keine else
-Bedingung haben.
5.3. with
with
wird wie die if
-Bedingung verwendet, testet aber zusätzlich, ob die Variable definiert ist.
my Int $var=1;
with $var {
say 'Hallo'
}
Wird der Code ohne Variablenzuweisung ausgeführt, sollte nichts passieren.
my Int $var;
with $var {
say 'Hallo'
}
without
ist die negierte Version von with
. Das läßt sich ähnlich unless
verstehen.
Wird die erste with
Bedingung nicht erfüllt, kann ein weiterer Block mit orwith
ausgeführt werden,
with
und orwith
können mit if
und elsif
verglichen werden.
5.4. for
Die for
Schleife iteriert über mehrere Werte.
my @array = [1,2,3];
for @array -> $array-wert {
say $array-wert*100
}
Hier haben wir die Interationsvariable $array-wert
erzeugt, um die Operation *100
auf jedem Array-Wert durchzuführen.
5.5. given
given
ist das Perl 6 eigene Switch Statement.
my $var = 42;
given $var {
when 0..50 { say 'Unter 50 oder gleich 50'}
when Int { say "ist ein Integer" }
when 42 { say 42 }
default { say "wie bitte?" }
}
Nach einem erfolgten Treffer, wird das Matching beendet.
Alternativ kann Perl 6 mit mit proceed
angewiesen werden, auch nach einem erfolgten Treffer weiterzumachen.
my $var = 42;
given $var {
when 0..50 { say 'Unter 50 oder gleich 50';proceed}
when Int { say "ist ein Integer";proceed}
when 42 { say 42 }
default { say "wie bitte?" }
}
5.6. loop
loop
ist eine Alternative zur for
Schleife.
Genaugenommen ist loop
die Sorte for
Schleife, wie sie gerne in der Familie der C-ähnlichen Sprachen verwendet wird.
Perl 6 gehört wohl auch in die Familie der C-ähnlichen Sprachen.
loop (my $i=0; $i < 5; $i++) {
say "Die aktuelle Zahl ist $i"
}
Für mehr Information zu Schleifen und Bedingungen, siehe http://doc.perl6.org/language/control |
6. I/O
In Perl 6 sind zwei der häufigsten Input/Output Interface das Terminal und Files.
6.1. Grundlegende I/O mit dem Terminal
6.1.1. say
say
schreibt auf den Standard Output. Es hängt noch ein Newline ans Ende an. Der folgende Code:
say 'Hallo die Dame.';
say 'Hallo der Herr.';
wird auf 2 voneinander getrennten Zeilen ausgegeben.
6.1.2. print
print
verhält sich genau wie say
, jedoch ohne die Newline.
Probieren Sie aus, was passiert, wenn Sie say
mit print
austauschen und vergleichen Sie die Ausgabe.
6.1.3. get
get
wird verwendet, Eingaben vom Terminal zu bekommen.
my $name;
say "Hallo, wie heißen Sie?";
$name=get;
say "Liebe(r) $name, wilkommen bei Perl 6";
Wenn das Skript ausgeführt wird, wartet das Terminal auf Die, den Namen einzugeben. Danach begrüßt das Skript Sie.
6.1.4. prompt
prompt
ist eine Kombination aus print
und get
.
Das obige Beispiel läßt sich auch so schreiben:
my $name = prompt("Hallo, wie heißen Sie? ");
say "Liebe(r) $name, wilkommen bei Perl 6";
6.2. Shell Kommandos ausführen
Zwei Subroutinen können verwendet werden, um Shell Kommandos auszuführen:
-
run
führt ein externes Kommando aus, ohne die Shell dafür zu verwenden -
shell
führt ein externes Kommando durch die System-Shell aus. Alle Shell Meta-zeichen werden von der Shell interpretiert, also auch Pipes, Redirects, Umgebungsvariablensubstitutionen usw.
my $name = 'Neo';
my $kommando = run 'echo', "Hallo $name";
my $kommando2 = shell "ls";
echo
und ls
sind gewöhnliche Shell Kommandos.
echo
gibt Text auf das Terminal aus (ähnlich say
und print
in Perl 6)
ls
gibt eine Liste aller Dateien und Verzeichnisse im aktuellen Verzeichnis aus
6.3. File I/O
6.3.1. slurp
slurp
wird verwendet um Daten aus einer Datei zu lesen.
Erstellen Sie eine Textdatei mit diesem Inhalt:
Hans 9
Hänschen 7
Hanna 8
Johanna 7
my $daten = slurp "datei.txt";
say $daten;
6.3.2. spurt
spurt
wird verwendet um Daten in eine Datei zu schreiben.
my $neue-daten = "Neue Bestmarken:
Paul 10
Paulina 9
Paula 11";
spurt "neue-datei.txt", $neue-daten;
Nach der Ausführung dieses Skripts wurde eine neue Datei namens neue-datei.txt erstellt. Sie enthält die neuen Bestmarken.
6.4. Umgang mit Dateien und Verzeichnissen
Perl 6 kann den Inhalt eines Verzeichnisses auch ohne Shell Kommandos (etwa ls
) lesen.
say dir; #Erstellt eine Liste der Dateien und Verzeichnisse im aktuellen Verzeichnis
say dir "/Documents"; #Erstellt eine Liste der Dateien und Verzeichnisse im angegebenen Verzeichnis
Noch dazu lassen sich auch Verzeichnisse neu anlegen und auch wieder entfernen.
mkdir "Neues_Verzeichnis";
rmdir "Neues_Verzeichnis";
mkdir
erstellt ein neues Verzeichnis.
rmdir
löscht ein bestehendes Verzeichnis, sofern es leer ist. Gibt eine Fehlermeldung aus, wenn es nicht leer ist.
Es kann auch geprüft werden, ob ein Pfad existiert, und ob es sich dabei um eine Datei oder ein Verzeichnis handelt:
In dem Verzeichnis, in dem Sie das gleich folgende Skript ausführen, erstellen Sie ein leeres Verzeichnis namens Verzeichnis123
und daneben eine leeree pl6 Datei namens Skript123.pl6
say "Skript123.pl6".IO.e;
say "Verzeichnis123".IO.e;
say "Skript123.pl6".IO.d;
say "Verzeichnis123".IO.d;
say "Skript123.pl6".IO.f;
say "Verzeichnis123".IO.f;
IO.e
prüft ob die Datei/das Verzeichnis existiert.
IO.f
prüft ob der Pfad eine Datei ist.
IO.d
prüft ob der Pfad ein Verzeichnis ist.
Für mehr Information zu I/O, siehe http://doc.perl6.org/type/IO |
7. Unterprogramme
7.1. Definition
Unterprogramm (auch Subroutinen, Routinen, Prozeduren oder Funktionen) sind eine Möglichkeit, ein Unterprogramm zu verpacken.
Die Definition einer Subroutine fämgt mit dem Stichwort sub
an. Nachdem sie definiert wurde, kann sie mit ihrem Namen aufgerufen werden.
Hier ein Beispiel:
sub ausserirdischer-grüßt {
say "Hallo Erdenbewohner";
}
ausserirdischer-grüßt;
Dies war ein Beispiel für eine Subroutine, die keine Eingabe benötigt.
7.2. Signatur
Viele Subroutinen brauchen Eingaben um zu funktionieren. Diese Eingaben erfolgen durch Argumente (auch Parameter genannt). Anzahl und Typen der Argumente, die eine Subroutine akzeptiert, nennen sich ihre Signatur.
Die folgende Subroutine akzeptiert ein Argument vom Typ String.
sub sag-hallo (Str $name) {
say "Hallo " ~ $name ~ "!"
}
sag-hallo "Paul";
sag-hallo "Paulina";
7.3. Multiple dispatch
Es lassen sich auch mehrere Subroutinen mit dem gleichen Namen aber verschiedenen Signaturen erstellen.
Wird die Subroutine aufgerufen, entscheidet die Laufzeitumgebung, welche der Subroutinen die geeignete ist anhand der Zahl und des Typs der mitgelieferten Argumente.
This type of subroutines is defined the same way as normal subs with the exception of swapping the sub
keyword with multi
.
multi grüße($name) {
say "Guten Morgen $name!";
}
multi grüße($name, $anrede) {
say "Guten Morgen $anrede $name!";
}
grüße "Hänschen";
grüße "Laura","Frau";
7.4. Voreingestellte und optionale Argumente
Wenn eine Subroutine definiert ist, ein Argument anzunehmen, und sie ohne eines aufgerufen wird, scheitert sie.
Alternativ bietet Perl 6 Möglichkeiten Subroutinen auszustatten mit:
-
Optionalen Argumenten
-
Voreingestellten (default) Argumenten
Optionale Argumente werden mit einem ?
nach dem Argumentnamen versehen.
sub sag-hallo($name?) {
with $name { say "Hallo " ~ $name ~ "!" }
else { say "Hallo Du!" }
}
sag-hallo;
sag-hallo("Laura");
Wenn der Anwender kein Argument angibt, kann ein voreingestelltes Argument verwendet werden.
Dazu übergibt man dem Argument in der Subroutinendefinition einen Wert.
sub sag-hallo($name="Matze") {
say "Hallo " ~ $name;
}
sag-hallo;
sag-hallo("Laura");
Für mehr Information zu Subroutinen und Funktionen, siehe http://doc.perl6.org/language/functions |
8. Funktionelles Programmieren
In diesem Teil geht es um Funktionelles Programmieren.
8.1. Funktionen sind First-Class-Objekt
Funktionen/Subroutinen sind First-Class-Objekt:
-
Sie können als Argument übergeben werden
-
Sie können von einer anderen Funktion übergeben werden
-
Sie können einer Variable zugewiesen werden
Das Konzept läßt sich besonders gut an der map
Funktion beschreiben.
map
ist eine Funktion höherer Ordnung, sie akzeptiert eine weitere Funktion als Argument.
my @array = <1 2 3 4 5>;
sub quadriert($x) {
$x ** 2
}
say map(&quadriert,@array);
(1 4 9 16 25)
Wir definierten eine Subroutine namens quadriert
, diese quadriert ihr Argument (rechnet das Argument hoch 2) für jedes numerische Argument, das sie erhält.
Dann haben wir die Funktion höherer Ordnung map
verwendet und ihr zwei Argumente übergeben, die Subroutine quadriert
und ein Array mit Zahlen.
Die Ausgabe ist eine Liste aller quadrierten Elemente des Arrays.
Wird eine Subroutine als Argument übergeben, muss ihrem Namen ein &
vorangestellt werden.
8.2. Closure
Alle Code-Objekte sind in Perl 6 Closures. Das bedeutet, sie können lexikalische Variablen eines äußeren Gültigkeitsbereichs (scope) referenzieren.
8.3. Anonyme Funktion
Eine anonyme Funktion wird auch Lambda genannt.
Eine anonyme Funktion ist nicht an einen Identifikator (identifier) gebunden, sie hat also keinen Namen.
Wir schreiben das map
-Beispiel mit einer anonymen Funktion:
my @array = <1 2 3 4 5>;
say map(-> $x {$x ** 2},@array);
Anstelle die Subroutine zu deklarieren und als Argument dem map
zu übergeben, haben wir sie direkt darin definiert.
Diese anonyme Subroutine -> $x {$x ** 2}
hat keinen Namen und kann nicht aufgerufen werden.
Im Perl 6 Jargon nennen wir diese Schreibweise einen spitzen Block (pointy block).
my $quadriert = -> $x {
$x ** 2
}
say $quadriert(9);
8.4. Verkettung (chaining)
In Perl 6 können Methoden verkettet werden, man braucht also nicht länger das Resultat einer Metode der nächsten als Argument zu übergeben.
Bei einem Array von werten sollen die eindeutigen Werte zurückgegeben werden, vom Größten zum Kleinsten sortiert.
Das kann man in etwa so lösen:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9 >;
my @fertiger-array = reverse(sort(unique(@array)));
say @fertiger-array;
Zuerst rufen wir die Funktion unique
auf @array
auf, dann übergeben wir das Ergebnis als Argument an sort
und dann übergeben wir dessen Sortierergebnis an reverse
.
Das obige Beispiel kann wie folgt mit Methodenverkettung geschrieben werden:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9 >;
my @fertiger-array = @array.unique.sort.reverse;
say @fertiger-array;
Man erkennt, dass sich verkettete Methoden leichter lesen lassen.
8.5. Feed Operator
Der Feed Operator, in einigen Funktionalen Programmiersprachten auch Pipe genannt, bietet eine noch bessere Visualisierung für Methodenverkettung.
my @array = <7 8 9 0 1 2 4 3 5 6>;
@array ==> unique()
==> sort()
==> reverse()
==> my @fertiger-array;
say @fertiger-array;
Fange mit `@array` an, dann gib eine Liste eindeutiger Elemente zurück
dann sortiere sie
dann reversiere sie
dann speichere das Ergebnis in @fertiger-array
Hier sieht man den Fluß der Methodenaufrufe von Oben nach Unten.
my @array = <7 8 9 0 1 2 4 3 5 6>;
my @fertiger-array-v2 <== reverse()
<== sort()
<== unique()
<== @array;
say @fertiger-array-v2;
Der Rückwärts Feed fungiert genau so wie der Vorwärts Feed, wird nur genau anders herum geschrieben.
Der Fluß der Methoden läuft dann von Unten nach Oben.
8.6. Hyperoperator
Der Hyperoperator >>.
ruft eine Methode aud allen Elementen einer Liste auf und gibt eine Liste mit allen Ergebnissen zurück.
my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub ist-gerade($wert) { $wert %% 2 };
say @array>>.is-prime;
say @array>>.&ist-gerade;
Mit dem Hyperoperator können bereits in Perl 6 definierte Methoden wie is-prime
aufgerufen werden, die beantworten, ob eine Zahl Primzahl ist oder nicht.
Weiter können wir neue Subroutinen definieren und sie mit dem Hyperoperator aufrufen. Dann muss ein &
vor den Namen der Methode vorangestellt werden: &ist-gerade
Dies ist eine praktische Möglichkeit, for
-Schleifen zum bearbeiten aller Werte zu vermeiden.
8.7. Verknüpfung (junction)
Eine Verknüpfung ist die logische Superposition von Werten.
Im Beispiel unten ist 1|2|3
eine Verknüpfung (junction).
my $wert = 2;
if $wert == 1|2|3 {
say "Der Wert ist 1 oder 2 oder 3"
}
Verwendet man Verknüpfungen, erhält man meistens Autothreading; die Operation wird für jedes Element der Verknüpfung ausgeführt und alle Ergebnisse werden zu einer neuen Verknüpfung zusammengefügt und die wird ausgegeben.
8.8. Gemütliche Lazy Listen
Eine gemütliche Lazy Liste ist eine Liste, die gemütlich/lazy ausgewertet wird.
Gemütliche/Lazy Auswertung vertagt die Berechnung eines Ausdrucks bis sie notwendig wird, und verhindert die Wiederholung der Berechnung bei sich wiederholenden Auswertungen in dem es sie in einer Lookup-Tabelle speichert.
Einige Vorteile sind dabei:
-
Die Berechung wird abgekürzt, indem ein paar unnötige Teilberechnungen vermieden werden.
-
Potentiell unendlich große Datenstrukturen können geschaffen werden
-
Der Kontrollfluß wird definiert
Um eine gemütliche Lazy Liste zu erstellen, verwendet man den Infixoperator …
Eine gemütliche Lazy Liste besteht aus Ursprungselement(en), einem Generator und einem Endpunkt.
my $lazyliste = (1 ... 10);
say $lazyliste;
Das Ursprungselement ist 1 und der Endpunkt ist 10. Kein Generator wurde definiert, daher ist der Standardgenerator der Nachfolger (+1)
Ander gesagt kann diese gemütliche/Lazy Liste (falls angefragt) diese Elemente zurückgeben: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
my $lazyliste = (1 ... Inf);
say $lazyliste;
Diese Liste kann (falls angefragt) jede ganze Zahl zwischen 1 und unendlich zurückgeben. Oder andersgesagt alle Ganzen Zahlen.
my $lazyliste = (0,2 ... 10);
say $lazyliste;
Die Ursprungselemente sind 0 und 2 und der Endpunkt ist 10.
Zwar wurde kein Generator definiert, jedoch kann Perl 6 aus den Ursprungselementen den Generator erkennen (+2).
Diese gemütliche Lazy Liste kann, wenn sie abgefragt werden, die folgenden Elemente zurückgeben (0, 2, 4, 6, 8, 10)
my $lazyliste = (0, { $_ + 3 } ... 12);
say $lazyliste;
In diesem Beispiel wurde der Generator definiert in { }
Diese gemütliche Lazy Liste kann, wenn sie abgefragt werden, die folgenden Elemente zurückgeben (0, 3, 6, 9, 12)
Wird ein definierter Generator verwendet, muss der Endpunkt einer der Werte sein, die der Generator zurückgeben kann. Alternativ kann man Dies hält den Generator nicht an.
Dies hält den Generator an.
|
9. Klassen & Objekte
Im vorangegangenen Kapitel wurde gelernt, wie Perl 6 Funktionales Programmieren erleichtert.
In diesem Kapitel sehen wir Objektorientiertes Programmieren in Perl 6.
9.1. Einleitung
Objektorientiertes Programmieren ist ein weitverbreitetes Paradigma.
Ein Objekt ist ein Set von miteinander verbundenen Variablen und Subroutinen.
Die Variablen nennt man Attribute und die Subroutinen nennt man Methoden.
Attribute definieren den Zustand (state) und Methoden definieren das Verhalten (behavior) eines Objekts.
Eine Klasse beschreibt die Struktur einer Menge von Objekten.
Um diese Beziehung zu verdeutlichen, verwenden wir ein Beispiel:
4 Leute halten sich in einem Raum auf |
Objekte ⇒ 4 Leute |
Diese 4 Leute sind Menschen |
Klasse ⇒ Mensch |
Sie haben unterschiedliche Namen, Alter, Geschlechter und Nationalitäten |
Attribute ⇒ Name, Alter, Geschlecht und Nationalität |
In Objektorientierter Lingo sagen wir, Objekte sind Instanzen einer Klasse.
Folgendes Beispiel:
class Mensch {
has $name;
has $alter;
has $geschlecht;
has $nationalität;
}
my $hans = Mensch.new(name => 'Hans', alter => 23, geschlecht => 'M', nationalität => 'deutsch');
say $hans;
Das Stichwort class
wird zur Definition einer Klasse verwendet.
Das Stichwort has
wird zur Definition der Attribute einer Klasse verwendet.
Die Methode .new()
nennt man einen Konstruktor. Er erschafft ein Object als eine Instanz der (ein Beispiel für die) Klasse mit der sie aufgerufen wurde.
Im obigen Skript enthält eine neue Variable $hans
eine Referenz auf eine neue Instanz von "Mensch" definiert durch Mensch.new()
.
Die Argumente, die an die Methode .new()
übergeben wurden, werden nun als Attribute des darunterliegenden Obbjekts verwendet.
Einer Klasse kann ein lexikalischer Gültigkeitsbereich mit my
gegeben werden:
my class Mensch {
}
9.2. Datenkapselung
Datenkapselung ist ein Konzept der Objektorientierung welches ein Set von Daten und Methoden zusoammenfasst.
Die Daten (Attribute) innerhalb eines Objekts sollten privat sein, also nur aus dem Objekt heraus zugänglich.
Um von außerhalb des Objekts an die Attribute darin zu gelangen, werden Methoden verwendet, die man Accessoren nennt.
Die beiden folgenden Skripte haben das selbe Ergebnis.
my $wert = 7;
say $wert;
my $wert = 7;
sub sag_wert {
$wert;
}
say sag_wert;
Die Methode sag_wert
ist ein Accessor. Sie läßt uns auf den Wert der Variable zugreifen, ohne dass wir direkten Zugriff darauf benötigen.
Datenkapselung wird in Perl 6 durch Twigillen vereinfacht.
Twigillen sind Sigillen zweiten Ranges. Sie werden zwischen Sigille und Attributnamen gesetzt.
Zwei Twigillen werden in Klassen verwendet:
-
!
deklariert explizit, dass ein Attribut privat ist. -
.
wird verwendet um automatisch einen Accessor für das Attribut zu erstellen.
Voreingestellt sind alle Attribute privat. Dennoch sei es eine gute Gewohnheit immer die !
Twigille zu verwenden.
Also sollten wir die obige Klasse besser so schreiben:
class Mensch {
has $!name;
has $!alter;
has $!geschlecht;
has $!nationalität;
}
my $hans = Mensch.new(name => 'Hans', alter => 23, geschlecht => 'M', nationalität => 'deutsch');
say $hans;
Wird dem Skript nun dieser Ausdruck hinzugefügt: say $hans.alter;
Das bewirkt diesen Fehler: Method 'alter' not found for invocant of class 'Mensch'
Grund ist, dass $!alter
privat ist und nur innerhalb des Objekts verwendet werden darf.
Es von Aussen zu probieren bewirkt einen Fehler.
Bei Ersetzen von has $!alter
mit has $.alter
ist zu sehen, dass say $hans.alter;
nun eine Ausgabe bewirkt.
9.3. Benannte und Positions- Argumente
In Perl 6 erben alle Klassen den standardmäßigen .new()
Konstruktor.
Dieser kann verwendet werden, Objekte zu erstellen, indem er mit Argumenten ausgestattet wird.
Er kann aber ausschließtlich mit benannten Argumenten verwendet werden.
Im obigen Beispiel ist zu erkennen, dass alle .new()
mitgegebenen Argumente benannt waren:
-
name => 'Hans'
-
alter => 23
Aber was, wenn nicht jedesmal die Namen aller Attribute mit übergeben werden sollen, wenn ein neues Objekt erstellt werden soll?
Dann muss ein neuer Konstruktor erstellt werden, der Positionsargumente akzeptiert.
class Mensch {
has $.name;
has $.alter;
has $.geschlecht;
has $.nationalität;
#neuer Konstruktor, der den Standardkonstruktor überschreibt.
method new ($name,$alter,$geschlecht,$nationalität) {
self.bless(:$name,:$alter,:$geschlecht,:$nationalität);
}
}
my $hans = Mensch.new('Hans',23,'M','deutsch');
say $hans;
Ein Positionsargumente akzeptierender Konstruktor wird, wie oben zu sehen, erstellt.
9.4. Methoden
9.4.1. Einführung
Methoden sind die Subroutinen eines Objekts.
Wie Subroutinen sind sie eine Möglichkeit, Funktionalität zu verpacken; sie nehmen Argumente an, haben eine Signatur und können als multi definiert werden.
Methoden werden mit dem Stichwort method
definiert.
Normalerweise werden Methoden gebraucht, um mit den Attributen eines Objekts eine Aktion durchzuführen.
Das erzwingt das Konzept der Datenkapselung. ObjektAttribut können nur von innerhalb des Objekts durch Methoden verändert werden.
Von Ausserhalb kann nur über die Objektmetoden interagiert werdern, es gibt keinen Zugang zu den Attributen.
class Mensch {
has $.name;
has $.alter;
has $.geschlecht;
has $.nationalität;
has $.geeignet;
method assess-geeignet {
if self.alter < 21 {
$!geeignet = 'Nein'
} else {
$!geeignet = 'Ja'
}
}
}
my $hans = Mensch.new(name => 'Hans', alter => 23, geschlecht => 'M', nationalität => 'deutsch');
$hans.assess-geeignet;
say $hans.geeignet;
Sobald Methoden innerhalb einer Klasse definiert sind, können Sie auf dem Objekt mit der Punktnotation aufgerufen werden:
object . methode oder wie in obigem Beispiel: $hans.assess-geeignet
Wenn in der Methodendefinition das Objekt selbst referenziert wird, verwendet man das Stichwort self
.
Soll in der Methodendefinition ein Attribut referenziert werden, verwendet man !
auch dann, wenn das Attribut mit .
definiert wurde.
Denn das .
Twigille deklariert ein Attribut mit !
und automatisiert dann auch noch die Erstellung des Accessors.
Im obigen Beispiel haben if self.alter < 21
und if $!alter < 21
den gleichen Effekt, obwohl sie sich im Detail unterscheiden:
-
self.alter
ruft die Methode.alter
auf (Accessor)
Kann auch als$.alter
geschrieben werden -
$!alter
ist ein Direkter Aufruf der Variable
9.4.2. Private Methoden
Normalerweise können Methoden auf Objekten von außerhalb der Klasse aufgerufen werden.
Private Methoden sind Methoden die nur von innerhalb der Klasse aufgerufen werden können.
Zum Beispiel wenn eine Methode eine andere für eine spezifische Aktion aufruft.
Die Methode, die mit der Welt um die Klasse herum interagiert ist öffentlich, die referenzierte Methode dagegen bleibt privat.
Sollen User sie doch nicht aufrufen dürfen, wird sie als privat deklariert.
Die Deklaration einer privaten Methode erfodert die Verwendung des !
Twigille vor ihrem Namen.
Private Methoden werden mit !
anstelle von .
aufgerufen
method !dies_ist_privat {
#code hier
}
method dies_ist_öffentlich {
self!dies_ist_privat;
#tut weiteres
}
9.5. Klassenattribute
Klassenattribute sind Attribute, die zur Klasse selbst gehören, nicht jedoch zu ihren Objekten.
Sie können in der Klassendefinition initialisiert werden.
KlassenAttribut werden mit my
anstelle von has
definiert.
Sie werden mit der Klasse selbst anstelle auf den Objekten aufgerufen.
class Mensch {
has $.name;
my $.zaehler = 0;
method new($name) {
Mensch.zaehler++;
self.bless(:$name);
}
}
my $a = Mensch.new('a');
my $b = Mensch.new('b');
say Mensch.zaehler;
9.6. Access Typ
Bis jetzt verwendeten alle Beispiele Accessoren um an Information aus den Attributen des Objekts zu gelangen.
Wie modifiziert man den Wert eines Attributs?
Dazu müssen wir es als lesen/schreiben bzw. read/write mit den Stichwörtern is rw
beschreiben.
class Mensch {
has $.name;
has $.alter is rw;
}
my $hans = Mensch.new(name => 'Hans', alter => 21);
say $hans.alter;
$hans.alter = 23;
say $hans.alter;
Standardmäßig werden alle Attribute als nur lesen bzw. read only deklariert, man kann es aber auch explizit mit is readonly
schreiben.
9.7. Vererbung
9.7.1. Einführung
Vererbung ist ein weiteres Konzept der Objektorientierten Programmierung.
Werden Klassen definiert, stellt sich schnell genug heraus, dass einge Attribute/Methoden in vielen Klassen vorkommen.
Sollte man Code duplizieren oder aber doch lieber wiederverwenden?
Letzteres! Man bedient sich der Vererbung.
Definieren wir je eine Klasse für Mensch und Angestellte.
Mensch
en haben 2 Attribute: name und alter.
Angestellte haben 4 Attribute: name, alter, firma und lohn
One would be tempted to define the classes as follow:
class Mensch {
has $.name;
has $.alter;
}
class Angestellter {
has $.name;
has $.alter;
has $.firma;
has $.lohn;
}
Obwohl dieser Codeschnipsel so gänzlich korrekt ist, ist das Konzept schwach.
Besser ist folgendes:
class Mensch {
has $.name;
has $.alter;
}
class Angestellter is Mensch {
has $.firma;
has $.lohn;
}
Das is
keyword definiert hier die Vererbung.
Im objektorientierungs-Jargon sagt man Angestellter ist eine Kindklasse von Mensch, und Mensch ist eine Elternklasse von Angestellter.
Kindklassen erben die Attribute und Methoden der Elternklasse, daher müssen diese nicht neu definiert werden.
9.7.2. Überschreiben
Kindklassen erben alle Attribute und Methoden aller Elternklassen.
Entsteht die Notwendigkeit, dass in der Kindklasse eine Methode ein anderes Verhalten zeigen soll, als das ererbte, muß sie in der Kindklasse neu definiert werden.
Dieses Konzept heißt Überschreiben.
Im folgenden Beispiel wird die Methode stell-dich-vor
in der Angestellter Klasse geerbt.
class Mensch {
has $.name;
has $.alter;
method stell-dich-vor {
say 'Hallo, ich bin ein Mensch, ich heisse ' ~ self.name;
}
}
class Angestellter is Mensch {
has $.firma;
has $.lohn;
}
my $hans = Mensch.new(name =>'Hans', alter => 23,);
my $anna = Angestellter.new(name =>'Anna', alter => 25, firma => 'Acme', lohn => 4000);
$hans.stell-dich-vor;
$anna.stell-dich-vor;
Das Überschreiben der Methode funktioniert so:
class Mensch {
has $.name;
has $.alter;
method stell-dich-vor {
say 'Hallo, ich bin ein Mensch, ich heisse ' ~ self.name;
}
}
class Angestellter is Mensch {
has $.firma;
has $.lohn;
method stell-dich-vor {
say 'Hallo, ich bin ein(e) Angestellte(r), ich heisse ' ~ self.name ~ ' und arbeite bei: ' ~ self.firma;
}
}
my $hans = Mensch.new(name =>'Hans',alter => 23,);
my $anna = Angestellter.new(name =>'Anna',alter => 25,firma => 'Acme',lohn => 4000);
$hans.stell-dich-vor;
$anna.stell-dich-vor;
Abhängig von der Klasse des Objekts wird die richtige Methode aufgerufen.
9.7.3. Untermethoden (SubMethoden )
Untermethoden (SubMethoden ) sind die Sorte Methode, die nicht an Kindklassen vererbt werden.
Auf sie kann nur von der Klasse zugegriffen werden, in der sie deklariert wurden.
Sie werden durch das Stichwort submethod
definiert.
9.8. Mehrfachvererbung
Mehrfachvererbung ist in Perl 6 erlaubt. Eine Klasse kann von vielen anderen Klassen erben.
class Balkendiagramm {
has Int @.balken-werte;
method plotte {
say @.balken-werte;
}
}
class Liniendiagramm {
has Int @.linien-werte;
method plotte {
say @.linien-werte;
}
}
class Kombidiagramm is Balkendiagramm is Liniendiagramm {
}
my $tatsächlich-verkauft = Balkendiagramm.new(balken-werte => [10,9,11,8,7,10]);
my $vorraussichtlicher-verkauf = Liniendiagramm.new(linien-werte => [9,8,10,7,6,9]);
my $tatsächlich-gg-vorraussichtlich = Kombidiagramm.new(balken-werte => [10,9,11,8,7,10],
linien-werte => [9,8,10,7,6,9]);
say "Tatsächlich Verkauft:";
$tatsächlich-verkauft.plotte;
say "Vorraussichtlicher Verkauf:";
$vorraussichtlicher-verkauf.plotte;
say "Tatsächlich gg. Vorraussichtlich:";
$tatsächlich-gg-vorraussichtlich.plotte;
Ausgabe
Tatsächlich Verkauft:
[10 9 11 8 7 10]
Vorraussichtlicher Verkauf:
[9 8 10 7 6 9]
Tatsächlich gg. Vorraussichtlich:
[10 9 11 8 7 10]
Die Klasse Kombidiagramm
soll zwei Serien enthalten,
eine für die tatsächlichen Werte in Balkendiagrammen geplottet,
und eine weitere für vorraussichtlichen Werte auf einer Linie geplotted.
Deswegen wurde sie als Kind von Liniendiagramm
und Balkendiagramm
definiert.
Dabei läßt sich feststellen, dass der Aufruf der Methode plotte
im Kombidiagramm
nicht das gewünschte Resultat erbracht hat, denn
nur eine Serie wurde geplottet.
Warum ist das geschehen?
Kombidiagramm
erbt von Liniendiagramm
und Balkendiagramm
, und beide haben eine Methode namens plotte
.
Wird diese Methode auf Kombidiagramm
aufgerufen, muß Perl 6 diesen Konflikt lösen, indem es eine der beiden geerbten Methoden aufruft.
Um korrekt zu funktionieren muß die Methode plotte
die Methode in Kombidiagramm
überschrieben werden.
class Balkendiagramm {
has Int @.balken-werte;
method plotte {
say @.balken-werte;
}
}
class Liniendiagramm {
has Int @.linien-werte;
method plotte {
say @.linien-werte;
}
}
class Kombidiagramm is Balkendiagramm is Liniendiagramm {
method plotte {
say @.balken-werte;
say @.linien-werte;
}
}
my $tatsächlich-verkauft = Balkendiagramm.new(balken-werte => [10,9,11,8,7,10]);
my $vorraussichtlicher-verkauf = Liniendiagramm.new(linien-werte => [9,8,10,7,6,9]);
my $tatsächlich-gg-vorraussichtlich = Kombidiagramm.new(balken-werte => [10,9,11,8,7,10],
linien-werte => [9,8,10,7,6,9]);
say "Tatsächlich Verkauft:";
$tatsächlich-verkauft.plotte;
say "Vorraussichtlicher Verkauf:";
$vorraussichtlicher-verkauf.plotte;
say "Tatsächlich gg. Vorraussichtlich:";
$tatsächlich-gg-vorraussichtlich.plotte;
Ausgabe
Tatsächlich Verkauft:
[10 9 11 8 7 10]
Vorraussichtlicher Verkauf:
[9 8 10 7 6 9]
Tatsächlich gg. Vorraussichtlich:
[10 9 11 8 7 10]
[9 8 10 7 6 9]
9.9. Rollen
Rollen sind Klassen ähnlich, denn sie sind eine Sammlung von Attribute und Methoden.
Rollen werden mit dem Stichwort role
deklariert und Klassen, die die Rolle implementieren können dies mit dem Stichwort does
.
role Balkendiagramm {
has Int @.balken-werte;
method plotte {
say @.balken-werte;
}
}
role Liniendiagramm {
has Int @.linien-werte;
method plotte {
say @.linien-werte;
}
}
class Kombidiagramm does Balkendiagramm does Liniendiagramm {
method plotte {
say @.balken-werte;
say @.linien-werte;
}
}
my $tatsächlich-verkauft = Balkendiagramm.new(balken-werte => [10,9,11,8,7,10]);
my $vorraussichtlicher-verkauf = Liniendiagramm.new(linien-werte => [9,8,10,7,6,9]);
my $tatsächlich-gg-vorraussichtlich = Kombidiagramm.new(balken-werte => [10,9,11,8,7,10],
linien-werte => [9,8,10,7,6,9]);
say "Tatsächlich Verkauft:";
$tatsächlich-verkauft.plotte;
say "Vorraussichtlicher Verkauf:";
$vorraussichtlicher-verkauf.plotte;
say "Tatsächlich gg. Vorraussichtlich:";
$tatsächlich-gg-vorraussichtlich.plotte;
Beim Ausführen dieses Skripts läßt sich erkennen: die Ausgabe ist die gleiche wie vorher.
Da sich offensichtlich Rollen genau wie Klassen verhalten, wofür braucht man sie dann?
Um das zu beantworten, ändert man das erste Skript so, dass es Mehrfachvererbung verwendet,
eben das Skript indem vergessen wurde, die Methode plotte
zu überschreiben.
role Balkendiagramm {
has Int @.balken-werte;
method plotte {
say @.balken-werte;
}
}
role Liniendiagramm {
has Int @.linien-werte;
method plotte {
say @.linien-werte;
}
}
class Kombidiagramm does Balkendiagramm does Liniendiagramm {
}
my $tatsächlich-verkauft = Balkendiagramm.new(balken-werte => [10,9,11,8,7,10]);
my $vorraussichtlicher-verkauf = Liniendiagramm.new(linien-werte => [9,8,10,7,6,9]);
my $tatsächlich-gg-vorraussichtlich = Kombidiagramm.new(balken-werte => [10,9,11,8,7,10],
linien-werte => [9,8,10,7,6,9]);
say "Tatsächlich Verkauft:";
$tatsächlich-verkauft.plotte;
say "Vorraussichtlicher Verkauf:";
$vorraussichtlicher-verkauf.plotte;
say "Tatsächlich gg. Vorraussichtlich:";
$tatsächlich-gg-vorraussichtlich.plotte;
Ausgabe
===SORRY!===
Method 'plotte' must be resolved by class Kombidiagramm because it exists in multiple roles (Liniendiagramm, Balkendiagramm)
Werden einundderselben Klasse mehrere Rollen zugewiesen und daraus entsteht ein Konflikt, wird ein Kompilierzeit-Fehler ausgegeben.
Dieser Ansatz ist deutlich sicherer als Mehrfachvererbung, bei der Konflikte keine Fehler sind und erst zur Laufzeit ausgewertet werden.
Kurz: Rollen warnen vor Konflikten.
9.10. Introspection
Introspection ist der Vorgang, etwas über ein Objekt zu erfahren, etwa seine Eigenschaften oder seine Attribute oder seine Methoden.
class Mensch {
has Str $.name;
has Int $.alter;
method stell-dich-vor {
say 'Hallo, ich bin ein Mensch, ich heisse ' ~ self.name;
}
}
class Angestellter is Mensch {
has Str $.firma;
has Int $.lohn;
method stell-dich-vor {
say 'Hallo, ich bin ein Angestellter, ich heisse ' ~ self.name ~ ' und arbeite bei: ' ~ self.firma;
}
}
my $hans = Mensch.new(name =>'Hans',alter => 23,);
my $anna = Angestellter.new(name =>'Anna',alter => 25,firma => 'Acme',lohn => 4000);
say $hans.WHAT;
say $anna.WHAT;
say $hans.^attributes;
say $anna.^attributes;
say $hans.^methods;
say $anna.^methods;
say $anna.^parents;
if $anna ~~ Mensch {say 'Anna ist ein Mensch'};
Introspection wird ermöglicht durch:
-
.WHAT
gibt die Klasse aus, von der das Objekt erzeugt wurde. -
.^attributes
gibt eine Liste aller Attribute des Objekts aus. -
.^methods
gibt alle Methoden aus, die auf dem Objekt aufgerufen werden können. -
.^parents
gibt alle Elternklassen jener Klasse aus, zu der das Objekt gehört. -
~~
wird der Smart-Match Operator genannt. Er evaluiert zu True falls das Object aus jener Klasse erstellt wurde, mit der es verglichen wird, oder aber aus einer Klasse, die von jener Klasse erbt.
10. Umgang mit Fehlermeldungen
10.1. Fehlermeldungen abfangen
Fehlermeldungen sind eine besondere special behavior die zur Ausführungszeit passiert, wenn etwas schief läuft.
Man sagt, Fehlermeldungen werden geworfen (Exceptions are thrown).
Zuerst ein problemlos laufendes Skript:
my Str $name;
$name = "Johanna";
say "Hallo " ~ $name;
say "Wie geht's?"
Ausgabe
Hallo Johanna
Wie geht's?
Nun folgendes Skript, dass eine Fehlermeldung ausgibt/eine Exception wirft:
my Str $name;
$name = 123;
say "Hallo " ~ $name;
say "Wie geht's?"
Ausgabe
Type check failed in assignment to $name; expected Str but got Int
in block <unit> at Fehlermeldungen.pl6:2
Immer, wenn ein Fehler passiert (in diesem Fall einem String eine Zahl zuweisen), wird das Programm beendet und weiterer Code wird nicht evaluiert, selbst wenn sie fehlerfrei sind.
Umgang mit Fehlermeldungen (Exception handling) ist der Vorgang, einen Fehler, der geworfen wurde abzufangen (catch
), damit das Programm weiterlaufen kann.
my Str $name;
try {
$name = 123;
say "Hallo " ~ $name;
CATCH {
default {
say "Sag Deinen Namen noch einmal, denn wir konnten ihn nicht in unseren Daten auffinden.";
}
}
}
say "Wie geht's?";
Ausgabe
Sag Deinen Namen noch einmal, denn wir konnten ihn nicht in unseren Daten auffinden.
Wie geht's?
Umgang mit Fehlermeldungen mit dem Try-Catch
Block.
try {
#Code hier
#falls etwas schief läuft, tritt das Programm in den CATCH Block ein
#falls nichts schief läuft, wird der CATCH Block igoriert
CATCH {
default {
#dieser Code hier wird nur ausgewertet, wenn eine Fehlermeldung geworfen wurde
}
}
}
Der CATCH Block kann genau wie ein given
Block definiert werden.
Das bedeutet, wir können viele Arten von Fehlern abfangen und verschieden behandeln.
try {
#Code hier
#falls etwas schief läuft, tritt das Programm in den CATCH Block ein
#falls nichts schief läuft, wird der CATCH Block igoriert
CATCH {
when X::AdHoc { #Tu etwas falls ein Fehler vom Typ X::AdHoc geworfen wurde }
when X::IO { #Tu etwas falls ein Fehler vom Typ X::IO geworfen wurde }
when X::OS { #Tu etwas falls ein Fehler vom Typ X::OS geworfen wurde }
default { #Tu etwas falls ein Fehler geworfen wurde, der zu keinem der obigen Typen gehört }
}
}
10.2. Fehlermeldungen werfen
Im Gegensatz zum abfangen von Fehlermeldungen erlaubt Perl 6 auch das explizite Werfen von Fehlermeldungen.
Zweierlei Arten von Fehlermeldungen können geworfen werden:
-
ad-hoc Fehlermeldungen
-
typisierte Fehlermeldungen
my Int $alter = 21;
die "Error !";
my Int $alter = 21;
X::AdHoc.new(payload => 'Error !').throw;
Ad-hoc Fehlermeldungen werden durch die Funktion die
geworfen, der die Nachricht der Fehlermeldung folgt.
Typisierte Fehlermeldungen sind Objekte, daher die Verwendung des .new()
Konstruktors im obigen Beispiel.
Alle typisierten Fehlermeldungen stammen von der Klasse X
ab, hier ein paar Beispiele:
X::AdHoc
ist der einfachste Typ Fehlermeldung
X::IO
gehört zu IO Fehlern
X::OS
gehört zu OS Fehlern
X::Str::Numeric
gehört dazu, dass versucht wurde, aus einem String eine Zahl zu machen
Für die vollständige Aufzählung von Fehlermeldungen und den dazugehörigen Methoden, siehe http://doc.perl6.org/type.html und navigiere zu den Typen, die mit X anfangen. |
11. Reguläre Ausdrücke (Regular Expressions)
Ein Regulärer Ausdruck (Regular Expression), auch Regex genannt, ist eine Folge von Zeichen, die auf ein Muster zutreffen sollen (pattern matching).
Am einfachsten stellt man sich solch ein Muster vor.
if 'Erleuchtung' ~~ m/ Erle / {
say "Erleuchtung enthält das Wort Erle";
}
Der Smartmatch-Operator ~~
wird hier verwendet, um zu prüfen, ob in der Zeichenfolge (Erleuchtung) das Wort (Erle) enthalten ist.
"Erleuchtung" wird gegen die Regex m/ Erle / gematcht
11.1. Regex Definition
Ein A Regulärer Ausdruck kann wie folgt definiert werden:
-
/Erle/
-
m/Erle/
-
rx/Erle/
Leerzeichen sind dabei normalerweise irrelevant, wenn es nicht anders spezifiziert wird, m/Erle/
und m/ Erle /
sind gleich.
11.2. Matching-Zeichen
Alphanumerische Zeichen und der Unterstrich _
werden so geschrieben, wie sie sind.
Alle sonstigen Zeichen müssen mit einem Rückwärtsschrägstrich (backslash) escaped werden oder von Anführungszeichen umgeben werden.
if 'Temperatur: 13' ~~ m/ \: / {
say "Die Zeichenfolge enthält einen Doppelpunkt (:)";
}
if 'Alter = 13' ~~ m/ '=' / {
say "Die Zeichenfolge enthält ein Gleichheitszeichen (=)";
}
if 'name@company.com' ~~ m/ "@" / {
say "Das könnte möglicherweise eine Emailadresse sein, denn es enhält das @ Zeichen";
}
11.3. Matching-Kategorien von Zeichen
Zeichen können in Kategorien eingeordnet werden, und wir können gegen diese matchen.
Wir können auch nach dem Inversen der Kategorie matchen (alles ausser diesem):
Kategorien |
Regex |
Invertiert |
Regex |
Wort-Zeichen (Buchstabe, Ziffer oder Unterstrich) |
\w |
Jedes Zeichen, das kein Wort-Zeichen ist |
\W |
Ziffer |
\d |
Jedes Zeichen, das keine Ziffer ist |
\D |
Whitespace-Zeichen |
\s |
Jedes Zeichen, das nicht Whitespace ist |
\S |
Waagerechte Whitespace |
\h |
Jedes Zeichen, das keine waagerechte Whitespace ist |
\H |
Vertikale whitespace |
\v |
Jedes Zeichen, das keine vertikale Whitespace ist |
\V |
Tabulator |
\t |
Jedes Zeichen, das kein Tabulator ist |
\T |
Zeilenumbruch |
\n |
Jedes Zeichen, das kein Zeilenumbruch ist |
\N |
if "Hans123" ~~ / \d / {
say "Das ist kein gültiger Name, Zahlen sind nicht erlaubt";
} else {
say "Das ist kein gültiger Name"
}
if "Hänschen-Klein" ~~ / \s / {
say "Diese Zeichenfolge enthält Whitespace";
} else {
say "Diese Zeichenfolge enthält keine Whitespace"
}
11.4. Unicode Eigenschaften (Unicode properties)
Matching gegen Kategorien von Zeichen, wie soeben gesehen, erscheint sehr praktisch.
Ein noch systematischerer Ansatz ist es, Unicode Eigenschaften zu verwenden.
Unicode Eigenschaften werdein in <: >
eingepackt.
if "Hans123" ~~ / <:N> / {
say "Enthält eine Zahl";
} else {
say "Enthält keine Zahl"
}
if "Hänschen-Klein" ~~ / <:Lu> / {
say "Enthält einen Großbuchstaben";
} else {
say "Enthält keinen Großbuchstaben"
}
if "Hänschen-Klein" ~~ / <:Pd> / {
say "Enthält einen Bindestrich";
} else {
say "Enthält keinen Bindestrich"
}
11.5. Platzhalter (Wildcards)
Platzhalter können auch in einer Regex verwendet werden.
Der Punkt .
matcht ein einzelnes Zeichen.
if 'abc' ~~ m/ a.c / {
say "Übereinstimmung";
}
if 'a2c' ~~ m/ a.c / {
say "Übereinstimmung";
}
if 'ac' ~~ m/ a.c / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
11.6. Quantifier
Quantifier folgen einem Zeichen und beziffern, wie oft es erwartet wird.
Das Fragezeichen ?
bedeutet genau keinmal oder genau einmal.
if 'ac' ~~ m/ a?c / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'c' ~~ m/ a?c / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
Das Sternchen *
bedeutet genau keinmal oder beliebig oft.
if 'az' ~~ m/ a*z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'aaz' ~~ m/ a*z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'z' ~~ m/ a*z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
Das Pluszeichen +
bedeutet mindestens einmal.
if 'az' ~~ m/ a+z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'aaz' ~~ m/ a+z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
if 'z' ~~ m/ a+z / {
say "Übereinstimmung";
} else {
say "Keine Übereinstimmung";
}
11.7. Übereinstimmungsergebnisse (Match Results)
Sobald ein Treffer einer Zeichenfolge gegen eine Regex erfolgreich ist,
wird das Übereinstimmungsergebnis in der besonderen Variable $/
abgelegt.
if 'Rakudo ist ein Perl 6 Kompiler' ~~ m/:s Perl 6/ {
say "Die Übereinstimmung ist: " ~ $/;
say "Die Zeichenfolge vor der Übereinstimmung ist: " ~ $/.prematch;
say "Die Zeichenfolge nach der Übereinstimmung ist: " ~ $/.postmatch;
say "Die übereinstimmende Zeichenfolge beginnt an der Stelle: " ~ $/.from;
say "Die übereinstimmende Zeichenfolge endet an der Stelle: " ~ $/.to;
}
Die Übereinstimmung ist: Perl 6
Die Zeichenfolge vor der Übereinstimmung ist: Rakudo ist ein
Die Zeichenfolge nach der Übereinstimmung ist: Kompiler
Die übereinstimmende Zeichenfolge beginnt an der Stelle: 15
Die übereinstimmende Zeichenfolge endet an der Stelle: 21
$/
liefert ein Match Objekt (die mit der Regex übereinstimmende Zeichenfolge) zurück.
Follgende Methoden können auf dem Match Objekt aufgerufen werden:
.prematch
gibt die Zeichenfolge vor der Übereinstimmung aus.
.postmatch
gibt die Zeichenfolge nach der Übereinstimmung aus.
.from
gibt die Startposition der Übereinstimmung aus.
.to
gibt die Endposition der Übereinstimmung aus.
Leerzeichen sind dabei für gewöhnlich irrelevant. Soll gegen eine Regex mit Whitespace darin gematcht werden, muß diese explicit angegeben werden. Das :s in der Regex m/:s Perl 6/ bewirkt, dass Whitespace beachtet und nicht mehr ignoriert wird.Alternativ hätte man die Regex als m/ Perl\s6 / schreiben und used \s verwenden können, wie es schon zuvor als Platzhalter für Whitespace.Enthält eine Regex mehr als nur einen Whitespace, ist :s effektiver als \s für jedes Whitespace einzusetzen.
|
11.8. Beispiel
Ist eine Mailadresse möglicherweise gültig oder sicherlich nicht?
Nur für dieses Beispiel nehmen wir einmal (fälschlich) an, das gültige Mailadresse so aussehen sollen:
Vorname [Punkt] Nachname [@] firma [Punkt] (com/org/net)
Diese Regex wird nur Beispielhaft verwendet, um Regex-Funktionalität in Perl 6 zu demonstrieren.
Sie ist viel zu ungenau und daher nicht ernsthaft brauchbar, um echte Mailadressen auf ihre Gültigkeit zu testen! So sollte man sie bitte nicht in der Produktion verwenden. |
my $email = 'Hans.Meier@example.org';
my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /;
if $email ~~ $regex {
say $/ ~ " ist eine gültige Mailadresse";
} else {
say "Diese Zeichenkette ist keine gültige Mailadresse";
}
Hans.Meier@example.org ist eine gültige Mailadresse
<:L>
matcht einen einzigen Buchstaben
<:L>` matcht einen einzigen Buchstaben oder mehrere +
`\.` matcht einen einzigen [Punkt] +
`\@` matcht ein einziges [@] +
`<:L:N>
matcht einen Buchstaben und eine Zahl
<:L+:N>+
matcht einen oder mehrere (Buchstaben und Zahlen)
Die Regex kann wie folgt auseinandergenommen werden:
-
Vorname
<:L>+
-
[Punkt]
\.
-
Nachname
<:L>+
-
[@]
\@
-
Firmenname
<:L+:N>+
-
[Punkt]
\.
-
com/org/net
<:L>+
my $email = 'Hans.Meier@example.org';
my regex viele-buchstaben { <:L>+ };
my regex punkt { \. };
my regex at { \@ };
my regex viele-buchstaben-zahlen { <:L+:N>+ };
if $email ~~ / <viele-buchstaben> <punkt> <viele-buchstaben> <at> <viele-buchstaben-zahlen> <punkt> <viele-buchstaben> / {
say $/ ~ " ist eine gültige Mailadresse";
} else {
say "Diese Zeichenkette ist keine gültige Mailadresse";
}
A benannte Regex wird mit dieser Syntax definiert: my regex regex-name { regex definition }
A benannte Regex wird mit dieser Syntax aufgerufen: <regex-name>
Für mehr Info über Regexen, siehe http://doc.perl6.org/language/regexes |
12. Perl 6 Module
Perl 6 ist eine Allzweck-Programiersprache. Sie kann für vielerlei Aufgaben verwendet werden, unter anderem: Textverarbeitung, Graphik, das Web, Datenbanken, Netzwerkprotokolle usw.
Wiederverwendbarkeit ist ein sehr wichtiges Konzept, dass es Programmierern erlaubt, das Rad nicht stetig immer wieder neu zu erfinden, wenn sie neue Aufgaben angehen.
Perl 6 ermöglicht das Erstellen und Verteilen von Modulen. Jedes Modul ist ein verpacktes Bündel von Funktionalität die wiederverwendet werden kann, sobald sie installiert wurde.
Zef ist ein Module-Management-Tool das mit Rakudo-Star mitgeliefert wird.
Um ein beliebiges Modul zu installieren, tippt man folgendes Kommando in ein Terminal:
zef install "module name"
Das Perl 6 Modul-Verzeichnis findet man unter: http://modules.perl6.org/ |
12.1. Module Verwenden
MD5 ist eine kryptographische Hashfunktion die einen 128-Bit Hashwert erzeugt.
MD5 hat eine Vielzahl von Anwendemöglichkeiten, nicht zuletzt dabei, einwegenkryptete Passwörter in einer Datenbank vorzuhalten.
Registriert sich ein neuer User, wird sein Passwort nicht im Klartext gespeichert, sondern gehasht.
Damit wird sichergestellt, dass auch wenn die Datenbank in falsche Hände geraten sollte, ein Angreifer nicht die Passwörter selbst erhalten wird.
Lets say you need a script that generates the MD5 hash of a password in preparation for storing it in the DB.
Zum Glück gibt es schon ein Perl 6 Modul, das den MD5 Algorithmus implementiert. Zeit es zu installieren:
zef install Digest::MD5
Dann das folgende Skript ausführen:
use Digest::MD5;
my $passwort = "passwort123";
my $passwort-gehasht = Digest::MD5.new.md5_hex($passwort);
say $passwort-gehasht;
Um die md5_hex()
-Funktion ausführen zu können, die die Hashs erstellt, muß das dafür notwendige Modul geladen werden.
Das Stichwort use
lädt das Modul, damit es im Skript verfügbar ist.
In Produktion reicht MD5 hashing alleine zur Sicherheit bereits nicht mehr aus, denn sie kann über Wörterbuchattacken geknackt werden. Sie sollte mit einem Salt https://en.wikipedia.org/wiki/Salt_(cryptography) kombiniert werden. |
13. Unicode
Unicode ist ein Standard, mit dem Text codiert und dargestellt werden kann, der die meisten Schreibsysteme der Welt ermöglicht.
UTF-8 ist eine Zeichenkodierung, die alle möglichen Zeichen oder code points, in Unicode kodieren kann.
Zeichen werden definiert durch:
ein Grapheme: Sichtbare Darstellung.
einen Codepoint: Eine Zahl, die dem (oder der das) Zeichen zugewiesen ist.
13.1. Unicode Verwenden
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";
Die obigen 3 Zeilen zeigen verschiedene Wege, Zeichen anzuzeigen:
-
Das Zeichen direkt zu schreiben (Graphem)
-
\x
und den Codepoint verwenden -
\c
und den Namen des Codepoint verwenden
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";
Der Buchstabe á
kann so geschrieben werden:
-
mit dem eindeutigen Codepoint
\x00e1
-
oder als Kombination der Codepoints
a
und Akut\x0061\x0301
say "á".NFC;
say "á".NFD;
say "á".uniname;
Ausgabe
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE
NFC
gibt den eindeutigen Codepoint.
NFD
teilt das Zeichen in die Codepoints seiner Teile auf und gibt diese aus.
uniname
gibt den Namen des Codepoints aus.
my $Δ = 1;
$Δ++;
say $Δ;
14. Parallelität, Parallele Programmierung und Ungleichzeitigkeit (Asynchrony)
14.1. Parallelität
Meistens laufen die Aufgaben innerhalb eines Programms nacheinander ab.
Das ist gut so, solange es nicht zu lange dauert.
Perl 6 hat Möglichkeiten, mehrere Dinge gleichzeitig zu erledigen.
Parallelität hat zweierlei Bedeutungen:
-
Parallele Ausführung: Mehrere unabhängige Kommandos werden gleichzeitig ausgeführt.
-
Datenparallelität: Ein einzelnes Kommando bearbeitet mehrere Elemente einer Liste gleichzeitig.
Letzteres zuerst.
14.1.1. Datenparallelität
my @array = (0..50000); # Array wird gefüllt
my @ergebnis = @array.map({ is-prime $_ }); # is-prime wird auf jedes Element aufgefrufen
say now - INIT now; # Gibt die Dauer des Programmlaufs aus
Es wird nur die einen Operation @array.map({ is-prime $_ })
durchgeführt.
Die Subroutine is-prime
wird sequentiell (also nach-und-nach) für jedes Element des Arrays aufgerufen:
Zuerst is-prime @array[0]
, dann is-prime @array[1]
und dann is-prime @array[2]
usw.
is-prime
auf mehrere Arrayelemente gleichzeitig angewendet werden:my @array = (0..50000); # Array wird gefüllt
my @ergebnis = @array.race.map({ is-prime $_ }); # is-prime wird auf jedes Element aufgefrufen
say now - INIT now; # Gibt die Dauer des Programmlaufs aus
Der Unterschied hier ist die Verwendung von race
in der Operation.
Diese erlaubt die gleichzeitige Verarbeitung mehrerer Arrayelemente.
Nach dem Ausführen beider Beispiele (mit und ohne race
), wie lang hat die Ausführung gedauert?
race
hyper
Nach dem Ausführen beider Beispiele (einmal mit |
14.1.2. Parallele Ausführung
my @array1 = (0..49999);
my @array2 = (2..50001);
my @ergebnis1 = @array1.map( {is-prime($_ + 1)} );
my @ergebnis2 = @array2.map( {is-prime($_ - 1)} );
say @ergebnis1 eqv @ergebnis2;
say now - INIT now;
-
2 Arrays wurden definiert.
-
Auf jedem der Arrays wurde einen andere Operation auf alle Elemente ausgeführt und die Ergebnisse gespeichert
-
Dann wurde geprüft, ob die Ergebnisse gleich waren
Das Programm wartet auf die Fertigstellung von @array1.map( {is-prime($_ + 1)} )
und führt erst dann @array2.map( {is-prime($_ - 1)} )
aus.
Beide Operationen werden jeweils auf ihr eigenes Array angewendet, und sind nicht voneinander abhängig.
my @array1 = (0..49999);
my @array2 = (2..50001);
my $versprechen1 = start @array1.map( {is-prime($_ + 1)} ).eager;
my $versprechen2 = start @array2.map( {is-prime($_ - 1)} ).eager;
my @ergebnis1 = await $versprechen1;
my @ergebnis2 = await $versprechen2;
say @ergebnis1 eqv @ergebnis2;
say now - INIT now;
Die Methode start
evaluiert den Code und gibt ein Versprechen-Objekt (an object of type promise) zurück bzw. in Kurzform ein Versprechen (promise).
Wird der Code korrekt ausgeführt, wird das Versprechen gehalten.
Wirft der Code einen Fehler, wird das Versprechen gebrochen.
Die Methode await
wartet auf ein Versprechen.
Wird dieses gehalten bekommt sie die zurückgegebenen Ergebnisse.
Wird es gebrochen, wird eine Fehlermeldung geworfen.
Wie lang dauerte der Programmlauf beider Skripte?
Parallelität brauche immer einen zusätzlichen Aufwand für das Threading. Immer wenn dieser größer wird, als die mögliche Zeiteinsparung, verlangsamt es das Skript sogar. Darum können race , hyper , start und await manche besonders einfachen Programme ausbremsen.
|
14.2. Parallele Programmierung und Ungleichzeitigkeit
Für mehr Information über Parallele Programmierung und Ungleichzeitigkeit, siehe http://doc.perl6.org/language/concurrency |
15. Die Community
Die Diskussion findet auf dem #perl6 IRC Kanal statt. Dort läßt sich jede Frage stellen:
http://perl6.org/community/irc
Hier gibt es Blog-Artikel zum Thema Perl 6:
http://pl6anet.org/ ist ein Perl 6 Blogaggregator.