Perl 6

Die Zukunft des Programmierens

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.

Hier gibt es Anweisungen, wie man Rakudo herunterladen und installieren kann (Englisch).

top

Was 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.

Als dynamisch typisierte Programmiersprache mit implizitem Speichermanagement ähnelt Perl eher den klassischen dynamischen Programmiersprachen wie Javascript, Ruby, Python und Lua.

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 Variablen beginnen mit einem Sigil. Ein Dollar $ steht dabei für einen sogenannten 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 einfügen, und auch die Befehle über mehrere Zeilen verteilen - nicht das Zeilenende, sondern der Strichpunkt definiert das Ende eines Befehls. Als Ausnahme davon gibt es die Regel, dass ein Strichpunkt optional ist, wenn nach einer schliessenden geschweiften Klammer eine neue Zeile anfängt.

Kommentare beginnen mit einem Hash # und erstrecken sich bis zum Ende der Zeile.

top

Variablen

Variablen sind Skalare, Arrays 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.

top

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 Objekte.

undef ist der default-Wert, und steht für "nicht zugewiesen".

top

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 Rechnen.

say exp(-1i * pi) + 1;

liefert also nicht genau 0, sondern eine sehr kleine, komplexe Zahl.

top

Strings

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. Variablennamen, die in dem String auftauchen, 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
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(' ');
# ("Der", "weite", "Weg")

$s.split("e");
# ("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.

top

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 initialisieren:

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, kann man mit eckigen Klammern ein nicht-interpolierendes Array-Objekt erzeugen:

my @a = 1, 2;
my @b = 0, [@a], 3;
say @b.elems;
# `3'

# oder kürzer:
@b = 0, [1, 2], 3;

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 Hashes %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.

top

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.

top

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.

top

While-Schleife

Wie fast jede Programmiersprache hat auch Perl 6 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 ausgefü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äherungsweise die Wurzel von $val aus (es gibt natürlich auch eine eingebaute Funtkion die das macht, sqrt().

top

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 Argumentliste 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, allerdings findet die Auswahl der aufzurufenden Variante (im Gegensatz zu C++) zur Laufzeit statt. 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.

top

Traits

Mit Traits, zu Deutsch "Eigenschaften" oder "Merkmale", kann man das Verhalten von Funktionen modifizieren. 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 offiziellen Perl 6-Dokumentation (Englisch).

top

Regulä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 Perl 5 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 überprüfen, ob ein Muster zu einer Zeichenkette passt.

Ein paar Beispiele erläutern 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.

top

Alternativen

Ein senkrechter Strich | steht für ein logisches Oder. m/Ein(horn|stein)/ passt also auf Einhorn und Einstein.

Verankerung

top

Wenn 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.

top

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.

top

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

SymbolBedeutung
.Beliebiges Zeichen
\sWhitespaces, also Leerzeichen, Tabs, Zeilenumbruch
\SAlles ausser Whitespaces
<sp>Ein Leerzeichen
\dEine Ziffer
\DAlles ausser einer Ziffer
\wEin Buchstabe
\WAlles ausser einem Buchstaben
\nEin Zeilenumbruch
\NAlle außer einem Zeilenumbruch
<alpha>Buchstabe (alternative Schreibweise)
<digit>Ziffer (alternative Schreibweise)

Man beachte dabei, dass "Buchstabe" 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.

top

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.

top

Benannte 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, erfolgt 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öffnet 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).

top

Aus Dateien / von der Standardausgabe lesen

Wenn man ein Filehandle hat, kann man daraus mittels get() zeilenweise lesen, oder mit getc() zeichenweise; lines gibt alle Zeilen zurück. 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 $*IN.lines -> 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.lines:

my $f = open("summe.dat") err die "Kann summe.dat nicht lesen: $!";
for $f.lines -> $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.

top

Mathematische 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 Bedingung 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 explizit 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ührt, 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;