Perl 6-Tutorial
Zielgruppe
Dieses Tutorial richtet sich an Interessierte, die schon in einer anderen Programmiersprache etwas programmieren können, aber bisher kein Perl können.
Compiler, Interpreter und VM
Perl 6 ist eine Sprachspezifikation, zu der mehrere Implementierungen in Entwicklung sind.
Vom Design her kann Perl 6 sowohl interpretiert als auch kompiliert
werden. Rakudo ist ein Perl 6-Compiler,
der Bytecode für die Parrot
Virtual Maschine erzeugt. Das geschieht aber Transparent für
den Benutzer, d.h. er ruft einfach perl6
scriptname auf, und dann führt perl6 das Skript
aus.
Die Implementierung von perl6 ist aber noch nicht sehr weit fortgeschritten. Am benutzbarsten ist zur Zeit eine andere Implementirung, Pugs, das "Perl User's Golfing System", ein in Haskell geschriebener Perl 6-Interpreter.
Zusammen mit Pugs werden umfangreiche Testcases entwickelt, die sicherstellen, dass Pugs und die entgültige Version von perl6 kompatibel bleiben.
Im Moment ist es sicher am einfachsten, sich pugs herunterzuladen und damit die Perl 6-Programme auszuführen.
topWas ist anders als in anderen Sprachen?
Perl 6 ist in einiger Hinsicht anders als "normale" Programmiersprachen. Viele kommen sicher daher, dass Perl nicht von Informatikern, sondern vor allem von einem Linguisten designed wurde.
Perl 6 bietet Kontextsensitivität. Das bedeutet, dass bestimmte Konstrukte (sowohl grammatikalische als auch semantische) je nach Umgebung eine andere Bedeutung haben können. Das soll größtmögliche Intuitivität erlauben.
Perls Variablen beginnen mit einem Sonderzeichen, Sigil genannt. Diese geben Auskunft über den (groben) Typ der Variable, und ermöglichen es, sie in Zeichenketten zu interpolieren.
Einfache Programme
So sieht ein kurzes Perl 6-Programm aus:
use v6; my $number = 2 * 3 + 5; say "Die Zahl ist ", $number;
Die Zeile use v6; sagt dem Compiler, dass es sich um
Perl-Code für Version 6 handelt. Wenn man probiert, den Code auf
einem älteren Perl-Interpreter auszuführen, wird er
vergeblich nach dem Modul (oder der Version) v6 suchen
und eine Fehlermeldung ausgeben.
Die Zeile
my $number = 2 * 3 + 5; deklariert mit dem
Schlüsselwort my eine Variable namens
$number, und weist ihr den Wert 11 zu.
Wie bereits besprochen beginnen alle Veriablen beginnen mit einem
Sigil. Ein Dollar $ steht dabei
für einen sogeannten Skalar, also ein einzelner Wert.
So ein Wert kann eine Zahl sein, ein String, eine Referenz oder ein Objekt. Es ist zwar möglich, genauere Typen anzugeben, aber es ist erforderlich.
Per Default wird automatisch zwischen Strings und Variablen
Konvertiert, say 3 * "20 Euro"; gibt also den Wert 60
aus (und eventuell eine Warnung, dass " Euro" nicht numerisch
ist).
Die letzte Zeile
say "Die Zahl ist ", $number; schließlich gibt den
String "Die Zahl ist " und danach die Variable
$number aus.
Man beachte, dass alle Befehle mit einem Strichpunkt
; aufhören.
Man kann beliebig viele Leerzeichen in Perl-Programme ausführen, und auch die Befehle über mehrere Zeilen verteilen - nicht das Zeilenende, sondern der Strichpunkt definiert das Ende eines Befehls. Ausnahme davon ist die Regel, dass ein Strichpunkt optional ist, wenn nach einer schliessenden geschweiften Klammer eine neue Zeile anfängt.
Kommentare beginnen mit einem Hash #.
Variablen
Variablen sind Skalare, Listen und Hashes. Außerdem gibt es ein paar seltener gebrauchte Variablentypen wie z.B. Package-Namen.
Dabei darf es durchaus zwei Variablen mit gleichem Namen aber aber unterschiedlichem sigil geben, ohne dass sie kollidieren.
my @namen = <Helmut Konrad Gerhard Angela>; my %namen = Helmut => "Kohl", Konrad => "Adenauer", Gerhard => "Schröder", Angela => "Merkel", ;
Hier sind @namen und %namen zwei verschiedene
Variablen, da das Sigil zum Name der Variable gehört.
Skalare
Skalare beginnen mit einem Dollar-Zeichen $, und
enthalten immer genau einen Wert.
Viele verschiedene Arten von Werten können in einem Skalar
gespeichert werden: undef, Zahlen, Strings, und
Referenzen auf Listen, Hashes und andere Objekete.
Zahlen
Am einfachsten zu verstehen sind sicherlich die Zahlen:
my $i = 1; my $f = -3.3; my $complex = 3 + 4i;
Eine Zahl kann eine ganze Zahl (Int), eine Fließkommazahl
(Num) oder eine Komplexe Zahl (Complex) sein.
Mit Zahlen kann man wie gewohnt rechnen, es stehen die Operatoren
+
(Addition), - (Substraktion), *
(Multiplikation), / (Divison) und **
(Potenzierung) zur Verfügung.
Wie gewohnt gilt Punkt- vor Strichrechnung, und Potenzierung hat noch höhere Priorität.
2 + 3 * 2**10 ist damit gleichwertig wie 2 + (3
* (2**10)).
Perl 6 kann auch mit komplexen Zahlen rechnen (wer nicht weiß, was das ist: diesen Abschnitt einfach ignorieren). Allerdings ist zu beachten, dass Perl 6 kein Computer-Algebra-System ist, d.h. es entstehen kleine Fehler beim Rechne.
say exp(-1i * pi) + 1;
liefert also nicht genau 0, sondern eine sehr kleine, komplexe Zahl.
topStrings
Strings oder Zeichenketten waren von jeher die Stärke von Perl, das hat sich auch in Perl 6 nicht geändert.
Strings kann man im Quelltext entweder in einfachen
Anführungszeichen 'bla' oder in doppelten
"bla" angeben.
Der Unterschied ist, dass Strings in doppelten Anführungszeichen "interpoliert" werden, d.h. andere Variablen werden durch ihren Wert ersetzt:
my $age = 18; say "In Deutschland wird man mit $age volljährig."; # gibt folgendes aus: # `In Deutschland wird man mit 18 volljährig.' (ohne die # Anführungszeichen) say 'In Deutschland wird man mit $age volljährig.'; # gibt folgendes aus: # `In Deutschland wird man mit $age volljährig.'
Zwei Strings können mit einer Tilde ~ miteinander
verbunden werden (Konkatenation):
say "Hallo" ~ " " ~ "Welt";
Aus Strings kann man andere Strings mit substr
ausschneiden:
use v6; my $alphabet = ("a" .. "z").join; say $alphabet; # `abcdefghijklmnopqrstuvwxyz' say $alphabet.substr(2); # alles ab dem 3. Zeichen, also #`cdefghijklmnopqrstuvwxyz' say $alphabet.substr(2, 5); # Fünf Zeichen ab dem 3. Zeichen, also `cdefg' # negative Indizes werden von hinten gezählt, mit dem letzen Element als # -1; sie müssen mit einem * gekennzeichnet werden: say $alphabet.substr(2, *-2); # Alles ausser den ersten beiden und den letzten # beiden Zeichen, also `cdefghijklmnopqrstuvwx'
Das erste Zeichen eines Strings hat den Index 0.
Man kann mit substr auch Text ersetzen:
$alphabet.substr(1, *-1) = " bis "; say $alphabet; # gibt `a bis z' aus
Strings kann man sehr leicht an bestimmten Stellen, zum Beispiel an bestimmten Zeichen, aufbrechen:
my $s = "Der weite Weg"; $s.split; # Ohne Argument spaltet split an Leerzeichen: # ("Der", "weite", "Weg") $s.split("e"); # mit Argument wird nach dem Argument gespalten: # ("D", "r w", "it", " W", "g")
split liefert eine Liste zurück, dazu später
mehr.
Mit split und einer ähnlichen Funktion namens
comb kann man Strings auch mit Hilfe von
regulären Ausdrücken zerlegen, was sehr
viele Möglichkeiten eröffnet.
Arrays und Listen
Listen enthalten mehrere Skalare in einer festen Reihenfolge.
Die Variablen, in denen Listen gespeichert werden, heissen Arrays. Ihr
Name beginnt mit dem Sigil @.
Wie Strings sind auch Arrays 0-indiziert, d.h. das erste Element hat den Index 0:
my @weekdays = "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"; # Zugriff auf einzelne Elemente: say @weekdays[0]; # `Montag' say @weekdays.join(", ") # `Montag, Dienstag, Mittwoch, Donnerstag, Freitag' say @weekdays.elems # 5
Es gibt auch eine Kurzschreibweisen, um Arrays zu initialiseren:
my @weekdays = <Montag Dienstag Mittwoch Donnerstag Freitag> # trennt am Leerzeichen my @digits = 0 .. 9 # selbsterklärend my @letters = "a" .. "z", "A" .. "Z" # ebenso ;-)
Aus Listen kann man sich aus Teillisten extrahieren ("Array Slices"):
my @a = 'A' .. 'Z'; say @a[0 .. 4]; # `ABCDE' say @a[0, -1]; # `AZ' # In Array Slices kann man auch schreiben: @a[1 .. 24] = " bis "; say @a; # `A bis Z'
Die Elemente, die durch das Schreiben in eine Array Slice
gelöscht werden, verschwinden nicht, sondern werden durch
undef, also einen leeren Wert, ersetzt.
Für den Zugriff und das Ändern des ersten und letzten Element gibt es eigene Funktionen, weil es so häufig gebraucht wird:
# push fügt hinten an, pop entfernt das letzte Element my @a = 1, 2; @a.push(3); # @a ist jetzt 1, 2, 3 say @a.pop; # gibt `3' aus, @a ist danach wieder 1, 2 # unshift fügt vorne an, shift entfernt das erste Element @a.unshift(0); # @a ist jetzt 0, 1, 2, 3 say @a.shift; # gibt `0' aus, @a ist danach (1, 2, 3)
Listen sind interpolierend, d.h. das einfügen von Listen in andere Listen erzeugt keine verschachtelten Listen:
my @a = 1, 2; my @b = 0, @a, 3; say @b.elems; # `4'
Um verschachtelte Listen zu bekommen, darf man keine Arrays einfügen, sondern Captures (Referenzen) zu Arrays.
Das erreicht man in Perl durch einen Rückstrich
\.
my @a = 1, 2; my @b = 0, \@a, 3; say @b.elems; # `3' # oder kürzer: @b = 0, [1, 2], 3;
Die Kurzschreibweise für Arrayreferenzen sind eckige
Klammern: [1 .. 4].
Auf verschachtelte Arrays kann man mit mehrfachen eckigen Klammern hintereinander zugreifen:
my @a = [1, 2, 3], [4, 5, 6], [7, 8, 9]; say @a[0][2]; # `3'top
Hashes
Hashes oder "Assoziative Listen" funktionieren ähnlich wie
Arrays, aber man greift mit Strings anstatt mit ganzen Zahlen auf die
ELemente zu. Ihr Sigil ist ein Prozent-Zeichen %. In anderen
Programmiersprachen heißen sie "dictionaries" oder "maps".
my %leader = ( 'USA' => 'G. W. Bush', 'Deutschland' => 'A. Merkel', 'GB' => 'G. Brown', ); my $country = "USA"; say $country , " wird zur Zeit von %leader{$country} regiert"; # Elemente im Nachhinein ändern oder hinzufügen: %leader{'Transsylvanien'} = 'Dracula';
Die Reihenfolge, in der die Elemente eingefügt werden, geht dabei verloren.
Pro Schlüssel gibt es genau einen Wert. Wenn man dem Schlüssel einen neuen Wert zuweist, wird der alte ersetzt.
Will man mehrere Werte pro Schlüssel speichern, so muss man in den Hash eine Liste schreiben:
my %regie = ( 'Steven Spielberg' => ['Die Vögel', 'München', 'Psycho'], 'Alfred Hitchcock' => ['The Kid', 'Goldrausch'], ); my $regisseur = "Alfred Hitchcok"; say $regisseur, " hat folgende Filme gedreht: ", %regie{$regisseur}.join(", ");
Auf alle Schlüssel eines Hashs %h kann man mit
%h.keys zugreifen, %h.values gibt alle Werte
zurück. Wie man elegant alle Elemente eines Hashes durchlaufen
kann, steht im Abschnitt über for-Schleifen.
Kontrollstrukturen
If-Else
Verzweigungen im Programmtext funktionieren mit der if ..
elsif .. else-Konstruktion:
if $age < 6 { say "Du bist noch nicht mal in der Schule" } elsif 11 <= $age <= 16 { say "Pubertaet..." } else { say "Irgendwer..." }
Der elsif und else-Block können
weggelassen werden, und es können beliebig viele
elsif-Blöcke vorkommen.
for: Über Listen iterieren
Häufig muss man alle Elemente einer Liste durchlaufen. Das
geht mit der for-"Schleife":
my @a = <Montag Dienstag Mittwoch Donnerstag Freitag>; for @a -> $day { say $day, "s frueh aufstehen..." } say "Immer das gleiche!";
Gibt als Ausgabe:
Montags frueh aufstehen... Dienstags frueh aufstehen... Mittwochs frueh aufstehen... Donnerstags frueh aufstehen... Freitags frueh aufstehen... Immer das gleiche!
Bei jedem Durchlauf der Schleife wird $day auf einen
Wert des Arrays gesetzt.
Wenn man die Angabe der Variable, in der der Wert gespeichert
werden soll, weglässt, so wird der Wert in der speziellen
Variable $_ gespeichert:
my @a = <Montag Dienstag Mittwoch Donnerstag Freitag>; for @a { say $_, "s frueh aufstehen..." } say "Immer das gleiche!";
Auch Hashes kann man damit durchlaufen:
my %essen = ( 'Schwaben' => 'Maultaschen', 'Bayern' => 'Weisswuerste', 'Briten' => 'Fish & Chips', ); for %essen.kv -> $leute, $gericht { say "Die $leute essen gerne $gericht"; }
Man beachte, dass die Elemente aus einem Hash in zufälliger Reihenfolge herauskommen. Wenn man das nicht möchte, kann man die keys vorher sortieren:
my %essen = ( 'Schwaben' => 'Maultaschen', 'Bayern' => 'Weisswuerste', 'Briten' => 'Fish & Chips', ); for %essen.keys.sort -> $leute { say "Die $leute essen gerne" ~ %essen{$leute}; }top
loop - Schleifen
Was in anderen Sprachen (C, C++, Java, Perl5 ...) die
for-Schleife ist, ist in Perl 6 die
loop-Schleife:
loop (my $i = 2; $i < 300; $i **= 2){ say $i } # Ausgabe: # 2 # 4 # 16 # 256
($i **= 2 ist kurz für $i = $i**2,
d.h. bei jedem Ausführen wird $i quadriert.)
loop ist wie folgt aufgebaut: loop(init;
bedingung; increment){ code }. Ganz am Anfang wird einmal die
Anweisung in init ausgeführt. Dann wird die
bedingung geprüft. Wenn sie wahr ist, wird
code ausgeführt, und dann increment.
Dann wird wieder die Bedingung geprüft, und so weiter.
While-Schleife
Wie fast jede Programmiersprache hat auch perl6 eine
while-Schleife:
use v6; my $val = 100; my $w = 1; my $eps = 1e-7; # berechne iterativ die Wurzel von $val: while abs($w*$w-$val) > $eps { $w = 0.5 * ($w + $val / $w ); } say $w; #Ausgabe: 10.00000000013989
Die while-Schleife wird augeführt, solange die
Bedingung direkt hinter dem while wahr ist.
Wenn die Bedingung von Anfang an nicht erfüllt ist, wir die Schleife kein einziges mal ausgeführt.
Diese Schleife rechnet Nährungsweise die Wurzel von
$val aus (es gibt natürlich auch eine eingebaute
Funtkion die das macht, sqrt().
Eigene Funktionen
Eigene Funktionen kann man mit sub-Schlüsselwort
schreiben:
sub factorial($x){ return [*] (1 .. $x); # [*] @liste liefert das Produkt aller Elemente der Liste } say factorial(3); # Ausgabe: `6'
In den runden Klammern hinter dem Funktionsnamen stehen die erwarteten Argumente. Wie bei "normalen" Variablen auch kann man den Typ mit angeben:
sub factorial(Int $x){ return [*] (1 .. $x); }
Und man kann auch den Rückgabetyp festlegen:
sub factorial(Int $x) returns Int { return [*] (1 .. $x); } # oder anders geschrieben: sub factorial(Int $x --> Int){ return [*] (1 .. $x); }
Wie in Perl 5 kann man die Argumenteliste bei der Definition der
Funktion auch weglassen, übergebene Argumente landen in der
speziellen Variable @_:
sub factorial { return [*] (1 .. @_[0]); }
Eine Funktion kann auch Listen zurückgeben. Innerhalb einer
Funktion kann man mit dem Objekt want abfragen, ob der
Aufrufer eine Liste, einen Hash oder ein einzelnes Element
erwartet.
sub wisdom { my @w = "Besser nie als spät", "Stoßstange ist aller Laster Anfang"; if (want.List){ return @w; } else { return "Wenn du zuhören würdest, hätte ich " ~ @w.elems ~ "Weisheiten für dich"; } }
Parameter werden per Default als read-only Referenzen
übergeben, d.h. die Funktion kann die Parameter nicht
verändern. Das kann man mit dem Trait is rw
ändern:
sub swap($x is rw, $y is rw){ ($x, $y) = ($y, $x); } # vertauscht die beiden übergebenen Argumente
Wenn man innerhalb der Funktion die Argumente ändern will, ohne
dass sich diese Änderung ausserhalb der Funktion auswirken, so kann man
das mit is copy erreichen:
my $a = 1; foo($a); say $a; # Ausgabe: 1 sub foo($x is copy) { $x = 2; }top
Multi subs
Multi subs funktionieren ähnlich wie überladene Funktionen in anderen Programmiersprachen. Mehrere Multi subs haben den gleichen Namen, aber unterschiedliche Signaturen:
multi foo(Str $x){ # ... Behandlung für Strings } multi foo(Int $x){ # Behandlung für ganze Zahlen }
Beim Aufruf mit foo($variable) wird anhand des Typs von
$variable entschieden, welche der Funktionen aufgerufen
wird.
Traits
Mit Traits, zu Deutsch "Eigenschaften" oder "Merkmale", kann man das Verhalten von Funktionen modifzieren. Ein Beispiel sind Vorbedingungen für die Ausführung einer Funktion, und Zusicherungen, die nach dem Ausführen einer Funktion gelten sollen:
sub wurzel(Num $x){ PRE { $x >= 0; } my $w = $x / 2; while abs($w * $w - $x) > 1e-8 { $w = 0.5 * ($w + $val / $w ); } POST { abs($x - $w * $w) <= 1e-8; } }
In diesem Beispiel wird eine einfache Wurzel-Funktion geschrieben,
die als Vorbedingung PRE hat, dass das Argument nicht
negativ sein darf. Wird wurzel mit einer negativen Zahl
aufgerufen, wird das Programm mit einer Fehlermeldung abgebrochen.
Analog dazu ist der POST-Block eine Zusicherung an den
Aufrufenden, dass die Wurzel mit einer Genauigkeit von mindestens
1*10-8 berechnet wurde.
Mehr zu Traits gibt es in der offziellen Perl 6-Dokumentation (Englisch).
topReguläre Ausdrücke / Rules
Ein Wort zu den Namen: Reguläre Ausdrücke, auf Englisch "Regular Expressions", sind ein bekanntes Konzept in der Informatik. Schon in Perl5 konnten die Regulären Ausdrücke mehr, als die Definition der Informatiker erlaubt, deswegen spricht man bei Perl von regexes, Singular regex. In Perl 6 können sie noch mehr (für Informatiker: sie entsprechen eher den Kontextfreien Sprachen), man nennt sie meistens Rules. Hier wird eine wilde Mischung dieser Begriffe gebraucht.
Reguläre Ausdrücke definieren Muster. Man kann den Interpreter anweisen, zu übeprüfen, ob ein Muster zu einer Zeichenkette passt.
Ein paar Beispiele erleutern das:
my $test_string = "Larry Wall hat Perl erfunden"; if $test_string ~~ m/ll/ { say 'Der Teststring enthält ein Doppel-l'; } if $test_string ~~ m/ar+y/ { say 'Der Teststring enthält ein a, mindestens ein r und dann ein y'; }
Einzelne Buchstaben, Ziffern, Minus - und Unterstrich
_ haben keine besondere Bedeutung, sondern stehen
für das jeweilige Zeichen.
Sonderzeichen wie ()[]{}+*? beraubt man ihrere besondern Bedeutung,
indem man ihnen eine Backslash \ voranstellt.
Leerzeichen innerhalb von Regexes werden ignoriert.
topAlternativen
Ein senkrechter Strich | steht für ein logisches
Oder. m/Ein(horn|stein)/ passt also auf Einhorn und
Einstein.
Verankerung
topWenn man eine regex auf einen String anwendet, passt sie, wenn
irgend ein Teilstring auf die regex passt. Wenn man sie explizit an
den Anfang oder das Ende eines Strings verankern will, kann man das
mit ^ und $ erreichen:
m/^GenaudieseZeile$/ # passt auf
Wenn ein String mehrere logische Zeilen enthält, kann man auf
Anfang und Ende einer Zeile mit ^^ und $$
überprüfen.
Quantoren
Quantoren sind Sonderzeichen, die angeben, wie häufig das vorhergehende Zeichen wiederholt werden kann.
Wenn hinter einem Buchstaben (oder einer Klammer) ein Fragezeichen
? kommt, so ist dieser Buchstaben optional.
m/mi?au/ passt also auf miau oder auf mau.
Das Plus + steht für eine oder beliebig viele
Wiederholungen. m/mi+au/ matched also miau, miiau, miiiau
usw, aber nicht mau.
Der Stern * steht für keine oder beliebig viele
Wiederholungen. m/mi*au/ matched also mau, miau, miiau,
miiiau etc.
Man kann auch explizit eine gewünschte Anzahl von
Wiederholungen angeben: m/ab**{3}c/ matched abbbc,
m/ab**{3 .. 8}c/ matched ein a, gefolgt von 3 bis 8 b's und
einem c.
Zeichenklassen
Bisher waren die regexes relativ langweilig. Durch Zeichenklassen
werden sie sehr viel mächtiger. Eine Zeichenklassen entspricht
einer (meistens recht langen) Auflistung von durch |
getrennten Zeichen, und matched immer genau ein Zeichen
| Symbol | Bedeutung |
|---|---|
| . | Beliebiges Zeichen |
| \s | Whitespaces, also Leerzeichen, Tabs, Zeilenumbruch |
| \S | Alles ausser Whitespaces |
| <sp> | Ein Leerzeichen |
| \d | Eine Ziffer |
| \D | Alles ausser einer Ziffer |
| \w | Ein Buchstabe |
| \W | Alles ausser einem Buchstaben |
| \n | Ein Zeilenumbruch |
| \N | Alle außer einem Zeilenumbruch |
| <alpha> | Buchstabe (alternative Schreibweise) |
| <digit> | Ziffer (alternative Schreibweise) |
Man beachte dabei, dass "Buchtstabe" nicht nur A bis Z meint, sondern
auch Buchstaben in anderen Sprachen. (Entspricht der Unicode-Eigenschaft
Letter).
Wenn man eine ganze Zahl, gefolgt von einem oder mehr Leerzeichen und einem Wort finden will, kann man das mit folgender Regex machen:
my $text = "Du schuldest mir 20 Euro"; if $text ~~ m/\d+\s+\w+/ { say "Aha, $/"; } # Ausgabe: 'Aha, 20 Euro'
In diesem Beispiel wird die spezielle Variable $/
verwendet, die, wenn sie als String verwendet wird, den gematched Text
zurückliefert.
Capturing Groups
Wenn man einen Teil einer regex in runde Klammern ()
einschliesst, merkt sich der Interpreter den String, der auf die
Teilregex innerhalb der Klammern gepasst hat.
Später innerhalb der regex kann man dann mit der Variable
$0 auf den Inhalt der ersten Klammer zugreifen, mit
$1 auf den Inhalt der zweiten usw. Außerdem kann
man $/ als Array verwenden, und auf das
$n-te Element zugreifen: $/[0],
$/[1] usw.
Damit kann man z.B. nach wiederkehrenden Mustern suchen. Angenommen, man will ganz einfaches HTML darauf untersuchen, ob die Tags richtig verschachtelt sind. Richtig wäre z.B:
Bla bla bla <strong>fetter Text</strong> bla bla
Und falsches HTML wäre
Bla bla bla <strong>fetter Text</em> bla bla
weil ein <strong>-Tag durch ein <em>-Tag
geschlossen wird.
Und so kann man nach korrekten Tags suchen:
my $text = 'Bla bla bla <strong>fetter Text</strong> bla bla'; if $text ~~ m/ \<(\w+)\> (.*) \<\/$0> / { say "Matched tag $0 with content '$1'"; } #Ausgabe: Matched tag strong with content 'fetter Text'
Wenn man den Inhalt einer Klammer nicht braucht, d.h. wenn man die
Klammer nur braucht, um zu gruppieren, kann man eckige Klammern
[...] anstatt runder Klammern verwenden.
Über Regexes kann man ganze Bücher schreiben, und in der Tat gibt es auch sehr gute Bücher darüber.
Gerade mit den Regexes von Perl 6 hat man noch sehr, sehr viel mehr Möglichkeiten, als bisher beschrieben.
Mehr dazu gibt es in dem Regex-Tutorial.
topBenannte Rules
Man kann Rules Namen geben, unter denen man sie ansprechen und in andere Rules einbauen kann:
token sigil { \$ | @ | % }; token twigil { \. | ! | \? | \* }; token identifier { <[a-zA-Z_0-9]> }; regex perl6_variable { <sigil> <twigil>? <identifier> };
Das ist ein krudes Modell, um Perl 6-Variablen aufzufinden, die aus
einem Sigil ($ für Skalare, @ für
Arrays...), einem optionalen Twigil und dem eigentlichen Namen der
Variablen.
Es demonstriert, wie man Rules mit dem Schlüsselwörtern
token und regex Namen geben kann, und dann mit
<name_der_rule> angesprochen werden kann.
Der Unterschied zwischen token und regex
ist, dass in einem token kein Backtracking stattfindet,
was die mögliche Komplexität einschränkt.
Damit kann man ganze Grammatiken aufbauen und mit rules überprüfen.
Rules anwenden
Die einfachste Möglichkeit, eine regexp/rule anzuwenden, ist der
"Smart Match Operator" ~~.
my $match = "abcd" ~~ m/b(c)/
in $match wird ein Match-Objekt
gespeichert. Diese Objekt verhält sich, je nach Kontext,
unterschiedlich:
say $match; # liefert `bc', also den Text, auf den die Regexp passt say $match[0]; # liefert `c', also den Text, auf den die erste Gruppe in runden Klammern # gepasst hat say $match.from, " ", $match.to; # liefert `1 3', also Anfangs- und Endposition des Matches if ($match){ # Im logik-Kontext (True/False) wird True zurückgegeben, # wenn die rexep auf den String gepasst hat ... }
Man kann auch mit regexes/rules Text ersetzen:
my $text = "foo 123 bar 42"; $text ~~ s/^f../blubb/; # $text enthält jetzt "blubb 123 bar 42" # jede Zahl im String verdoppeln: $text ~~ s:g[(\d+)] = 2 * $0; # das :g sorgt dafür, dass alle Vorkommnisse der Regex # ersetzt werden (g = global) say $text; # blubb 246 bar 84
Input/Output
Die Ausgabe auf den Bildschirm, d.h. auf die Standardausgabe des
Programms, erfolt mit say oder print. Der
Unterschied besteht darin, dass say am Ende einen
zusätzlichen Zeilenumbruch ausgibt, print nicht.
print "Text"; print ", und mehr Text\n"; # liefert `Text, und mehr Text' und dann einen Zeilenumbruch, d.h. die # nächste Ausgabe findet auf der nächsten Zeile statt.top
In Dateien schreiben
Ausgabe in Dateien ist auch nicht viel schwerer:
my $filename = "/path/to/file" my $f = open($filename, :w) err die "Can't open $filename: $!"; say $f: "Dieser Text landet in der Datei $filename"; $f.say: "Dieser Text auch"; $f.close();
Mit open wird eine Datei geöffnet, das zweite
Argument :w gibt an, dass die Datei zum schreiben
geöffent wird. Dabei wird sie, falls sie existiert,
überschrieben. Wenn man das nicht will, kann man sie mit
:a öffnen, dann wird alles, was hineingeschrieben
wird, ans Ende angehängt.
open() liefert ein filehandle zurück, dass man in
einem Skalar speichern kann, hier $f.
Mit der Konstruktion open(..) err die "..." wird im
Fehlerfall die Funktion die ausgeführt, die das
Programm mit einer Fehlermeldung beendet.
Man kann, wie oben im Beispiel, say und
print als erstes Argument ein Filehandle mitgeben,
gefolgt von einem Doppelpunkt :, um die Ausgabe in diese
Datei umzuleiten.
In jedem Programm gibt es die globalen Variablen
$*IN (default input oder stdin),
$*OUT (default output oder stdout) und
$*ERR (default error output oder stderr). (Man beachte
den Stern * nach dem Sigil, ein sogenanntes "secondary
sigil" oder "twigil", das alle globalen Variablen haben).
Aus Dateien / von der Standardausgabe lesen
Wenn man ein Filehandle hat, kann man daraus mittels
readline() zeilenweise lesen, oder mit
getc() zeichenweise. Mit slurp kann man den
gesamten Inhalt auslesen.
# lese durch beliebige Zeichen getrennte ganze Zahlen von der # Standardeingabe ein und gebe die Summe aus: for =<> -> my $line { # comb liefert alle Matches der Regular Expression zurück: my @numbers = $line.comb(/\d+/); # [+] (liste) gibt die Summe aller Elemente der Liste say [+] @numbers; }
Um aus einer Datei zu lesen, öffnet man sie vorher mit
open, und list mit der Konstruktion =$fh:
my $f = open("summe.dat") err die "Kann summe.dat nicht lesen: $!"; for =$f -> $line { my @numbers = $line.comb(/\d+/); say [+] @numbers; # [+] (liste) gibt die Summe aller Elemente der Liste }top
Objektorientierung: Klassen, Rollen und Objekte
Perl 6 bietet die Möglichkeit, objektorientiert zu programmieren. Allerdings ist das vollkommen optional, wenn man nicht will, muss man auch nicht.
Das Objektmodell von Perl 6 ist ausführlich hier beschrieben.
Funktionen
Perl 6 bring von sich aus viele nützliche Funktionen mit. Hier seien nur ein paar wichtige erwähnt, die komplette Liste inklusive Erklärungen gibt es in der offiziellen Dokumentation im Abschnitt builtin functions.
topMathematische Funktionen
Die "üblichen" mathematischen Funktionen sind in Perl 6 verfügbar:
abs (Betrag), floor, ceiling, round
(Runden), sin, cos, tan, asin, acos, atan, sec, cosec, cotan,
asec, acosec, acotan, sinh, cosh, tanh, asinh, acosh, atanh, sech,
cosech, cotanh, asech, acosech, acotanh (Trigonometrische
Funktionen, sec ist 1/sin, exp, log,
log10 (Exponent und Logarithmen), rand
(Zufallszahlen), sqrt, roots (Wurzel und
n-te komplexe Wurzeln) und sign
(Vorzeichen).
Listen-Behandlung
push, pop, shift und
unshift fügen an Anfang und Ende eines Arrays Elemente
hinzu und entfernen sie:
my @a = 1, 2, 3, 4, 5; my $b = @a.shift; # $b enthält 1, @a = (2, 3, 4, 5); @a.unshift(7); # @a ist jetzt (7, 2, 3, 4, 5) my $last = @a.pop; # @a ist (7, 2, 3, 4), $last ist 5 @a.push(23, 42); # @a ist jetzt (7, 2, 3, 4, 23, 42)
splice entfernt Elemente aus einem Array, und fügt
optional welche an. Der erste Parameter für splice ist
die Position, der zweite die Länge, der dritte (optional) die neuen
Elemente:
my @a = 1 .. 4; my @b = @s.splice(1, 2); # @b = (2, 3); @a = 1, 4; @a.splice(1, 0, @b); # @a ist wieder (1, 2, 3, 4); 0 Elemente ersetzen ist wie Elemente einfügen
grep liefert die Elemente einer Liste, die einer
bestimmten Bedinung genügen:
my @l = grep { $_ % 3 == 0}, 1 .. 10; # alle durch drei teilbaren Zahlen, also (3, 6, 9) #das gleiche: my @l = grep { $^a % 3 == 0}, 1 .. 10;
first funktioniert wie grep, nur dass nur
das erste passende Element zurückgeliefert wird.
pick liefert eine zufällige Auswahl an
Elementen:
(1 .. 10).pick(3) # (6, 1, 2) (1 .. 10).pick(3) # (7, 4, 9) (1 .. 10).pick(3) # (3, 5, 10)
map wendet eine Funktion auf jedes Element einer Liste
an, die zurückgegebenen Elemente bauen wieder eine Liste auf:
# Quadrate berechnen: (1 .. 10).map( {$_ * $_ }) # (1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
sort sortiert listen, optional mit einem
Vergleichs-Block:
(1 .. 20).pick(5).sort # (1, 2, 6, 15, 18) (1 .. 20).pick(5).sort # (5, 6, 15, 16, 19) # mit einem Vergleichsblock, der explit nach String-Reihenfolge # sortiert, d.h. "1" < "10" < "2": (1 .. 20).sort( {~$^a cmp ~$^b } # (1, 10, 11, 12 .. 19, 2, 20, 3, 4 .. 9)
In dem letzten Beispiel werden dem Vergleichsblock von
sort zwei Parameter übergeben, die in $^a
und $^b gespeichert werden.
min und max liefern das kleinste/größte
Element einer Liste, reverse ergibt die Liste in
umgekehrte Reihenfolge.
Timtowtdi: "There is more than one way to do it"
Eine von Perls Philosophien war schon immer, dem Programmierer möglichst viele Freiheiten zu lassen, und ihn damit so programmieren lassen, wie ihm oder ihr das am besten gefällt.
Hier seien ein paar Beispiele aufgefürht, wie man ein paar einfache Aufgaben auf verschiedene Arten lösen kann.
Alle Werte einer Liste um $n erhöhen
my $n = 3; my @a = (1, 4, 7, 9); #mit einer Schleife: my @b; for @a -> $i { push @b, $i + $n; } # oder so, wie man das in C machen würde: my @c = @a; loop(my $i = 0; $i < @a.elems; $i++){ @c[$i] += $n; } # Aus funktionalen Programmiersprachen "geklaut": my @d = map { $_ + $n }, @a; # Mit Perls Hyperoperatoren: my @e = @a >>+ $n;