Perl 6
Perl 6 Tutorial Part 7: Revision 2

=head1 Perl 6 Tutorial - Teil 7 : Text, Regeln und Grammatik

Willkommen zum siebenten Teil dieses poetischen Perl 6-Tutorials, daß vor allem
den Umgang mit Text, also Strings, zum Inhalt hat. Das klingt stark nach Regex,
beinhaltet aber noch einiges mehr, wie I<quoting>, Interpolation und Formate.
Beginnen wir jedoch mit den einfachsten Grundlagen.

=head2 Es werde String

Wie im zweiten Kapitel beschrieben, ist für Perl 6, ebenso wie in Perl 5, String
nur ein Kontext, in den beliebige Daten umgewandelt werden können. Dieser Kontext
wird aber nicht nur mit Operatoren erzeugt die mit C<~> beginnen, sondern auch
wenn ein Text ganz einfach in Anführungszeichen in den Quellcode eingefügt wird.

=head2 Einfaches Quoting

Die schlichte Variante sind dabei (wie bekannt) die einfachen Anführungszeichen,
innerhalb derer jedes Zeichen wörtlich (I<literal>) verstanden wird und besondere
Bedeutungen aufgehoben sind. Die einzigen beiden Ausnahmen sind C<\'> und C<\\>.
Ersteres ist notwendig um ein einfaches Anführungszeichen innerhalb des Strings
zu erhalten, zweiteres für einen umgekehrten Schrägstrich (I<Backslash>), der
normalerweise die besondere Bedeutung der Steuerzeichens (Metazeichens) aufhebt,
was auch I<escapen> genannt wird. Etwas in Anführungszeichen zu setzten wird
auch I<quoting> (Zitieren) bezeichnet, weswegen ein C<q//> die generalisierte
Form von C<''> ist.

say '\''; # sagt: '
say q/\\/; # sagt: \
say q["\\"]; # dito, beliebige Klammern sin möglich

Das schöne an Perl 6 ist allerdings nicht nur daß es sich hier bequem an Perl 5
angleicht, sondern daß das Bekannte (oft) zur allgemeingültigen Grundregeln wird.
C<''> hat überall die gleiche Bedeutung, auch innerhalb eines regulären Ausdrucks.
Und C<q//> ist die Basis sämtlicher I<quoting>-Operatoren die lediglich C<q//> mit
verschiedenen Optionen aufrufen. Aber um wirklich ehrlich zu sein: auch das ist
nur die halbe Wahrheit. Die echte Urvater aller I<quoting>-Operatoren ist das
C<Q>, welches gar nichts interpoliert und C<q//> ist ein I<Alias> auf C<Q :q //>.
Der Doppelpunkt vor dem "q" markiert einen benannten Parameter. Die ganze
Erklärung dazu steht in der vierten und fünften Folge.

=head2 Komplexeres Quoting

Auch wenn die Einfachen oft ausreichend sind und minimal Rechenzeit sparen,
so sieht man öfter die doppelten Anführungszeichen. Ihre Bedeutung blieb auch
im Wesentlichen unberührt. Innerhalb von C<""> werden Variablen evaluiert (durch
ihren Inhalt ersetzt) und I<Escapesequenzen> wie C<\n> (Zeilenende) oder C<\t>
(Tab) gegen entsprechende Sonder- oder Steuerzeichen ersetzt. Hinzu kam die an
Ruby erinnernde Möglichkeit, neben Subroutinen (hier mit "&" schreiben) nun auch
Blöcke (I<Closures>) auszuführen.

say "Und die Zusatzzahl für $heute lautet: \t { int rand(47) }.";

Auch hier hat Larry konsequent vereinheitlicht. Blöcke werden nun mal (wenn sie
kein Parameter sind) sofort ausgeführt. Und auch wer jetzt innerhalb einer Regex
etwas ausführen möchte sollte sich dieses Mittels bedienen anstatt sich mit der
Option "e" oder C<(?{ ... })> einen Kompilererror einzufangen. Anstatt "ee" läßt
sich ein C<eval> in den Block einfügen (Ausführung des Ausführungsergebnisses).

Damit ist eindeutig klar: C<""> oder alternativ auch C<qq//> ist ein I<Alias> auf
C<Q :s :a :h :f :c :b //>, oder? Verständlicher wird es, wenn die "langen Formen"
der "Adverbien" verwendet werden (C<Q :scalar :array :hash :function :closure
:backslash //>). C<:scalar> besagt das Skalare evaluiert werde, C<:array> - Arrays
... und C<:backslash> bezieht sich auf die Steuerzeichen. Das impliziert auch
C<:q> aka C<:single>, daß hierfür nicht extra angegeben werden mußte. C<:qq>
lautet ausgeschrieben entsprechend C<:double>. Weitere I<quote>-"Adverbien" sind
C<:x> aka C<:exec>, was dem bekannten Perl 5-C<qx> entspricht, nur das C<qx> in
Perl 6 logischerweise nicht interpoliert, da es gleich C<q :x //> ist. C<:w> aka
C<:words> ist ebenfalls altbekannt, wird aber in Perl 6 idiomatisch "< >"
geschrieben (siehe dritte Folge).
C<:ww> aka C<:quotewords> entspricht dem bereits bekannten "<< >>".

my $ich = 'Prinz';
my @n = 1 .. 5;
say q:a/$ich sage: in \@n ist @n./
# sagt: $ich sage: in @n ist 1 2 3 4 5.

=head2 Heredocs

I<Heredocs> sind nun auch nichts besonderes (eigenständiges) mehr, nur noch normale
I<quotings>, deren Ende mit einer definierten Zeichenkette markiert ist. Diese
kann nun beliebig eingerückt sein. I<Heredocs> werden damit weit mächtiger, ohne
erhöhten Lernaufwand.

my $letter = qq:to/END/
Dear $recipient:
Thanks!
Sincerely,
$me
END

Die Langform des C<:to>-Adverbs ist C<:heredoc>.

=head2 Die neuen Regex

Es gibt von diesen Adverbien noch einige mehr, diese haben jedoch nur eine lange
Form und nicht mehr direkt etwas mit Strings zu tun, da sie zu Code oder Regex
evaluiert werden. Denn Reguläre Ausdrücke werden in Perl 6 nicht mehr als Strings
angesehen, sondern als eigenständige Sprache innerhalb der Sprache (wie in Rebol).
Diese Sprache ist kontextfrei, was rekursive Ausdrücke erlaubt, wie erst seit
Perl 5.10 möglich. Und diese Sprache unterscheidet sich teilweise so stark von den
Regex in Perl 5, daß eine Zeit lang für sie ein neuer Begriff geprägt wurde (die
Perl "rules"). Somit wandelte sich der Ausdruck "Perl rules" von einer Angeberei
zur Erwähnung einer normalen Tatsache. Mittlerweile setzte sich aber auch für die
neuen Regeln der Begriff Regex wieder durch.

Es war nötig sie von Grund auf neu zu gestalten, da der Regex-Syntax mit der Zeit
zu unübersichtlich und widersprüchlich wurde. Dies war auch nur teilweise das
Verschulden von Larry oder Ilya, da viele Unsauberheiten der UNIX-Kultur
entstammen. Damals schien es noch eine gute Idee sich an die Standards zu halten
und Perl leicht erlernbar zu machen. Mittlerweile hat Perl selbst den Standard
gesetzt (PCRE) und es wird Zeit hier einiges gerade zu rücken.

Eine Regex kann mit drei Schreibweisen in einem Skalar gespeichert werden:

my $rx = rx[abc];
my $rx = q:regex[abc];
my $rx = regex{abc};

Die dritte Variante ist aber nicht ganz das Selbe wie die ersten Beiden und kann
daher nur ausschließlich mit geschweiften Klammern geschrieben werden. Mehr dazu
wenn es um "Grammatiken" geht. Diese Skalare "wissen" ob sie ein Regexobjekt oder
einen einfachen String enthalten und deshalb wird die Schreibweise C<\Q$var\E>
überflüssig. Auch ohne ihr würde eine Regex die aus Teilausdruck und einem String
wie folgende funktionieren.

$text ~~ m/ $rx $str /;

Oft werden Regex nicht gespeichert sondern direkt angewendet, was immer noch mit
C<m/.../> oder C<s/.../.../> oder den Sonderformen C<mm/.../> und C<ss/.../.../>
getan wird. Auch C<tr/.../.../> gibt es weiterhin.
Diese Operationen werden wie in Perl 5.10 mit dem I<Smartmatch> (~~) an einen
String gebunden. Das Resultat ist allerdings hier ein I<Matchobjekt>, daß im
boolschen Kontext zu C<Bool::True> oder C<Bool::False> evaluiert, damit C<if>,
C<when>, C<while> und C<until>, die diesen Kontext erzwingen, weiterhin arbeiten.
Das zuletzt im aktuellen Block erzeugte I<Matchobjekt> liefert auch die Variable
C<$/>, die alle Detailinformationen enthält. Andere, bisherige Sondervariablen
wurden abgeschafft. Allerdings sind C<$0> bis C<$9> I<Aliase> auf C<$/0> bis
C<$/9>. Als Trenner können weiterhin beinah beliebige Zeichen verwendet werden.
Aber Optionen werden nun (wie beim I<quoting>) als Adverbien vor die Trenner und
hinter den Namen der Operation gesetzt.

=head2 Regex Optionen

my $text = "Ich schreibe gerne udn schnell udn viel.";
$text ~~ s:g/udn/und/;

Ansonst sollte die Option ":g" bekannt sein. Ohne sie wird nur einmal ersetzt,
mit ihr, jedes Aufkommen. Auch die ":i"-Option, welche die Gross/Kleinschreibung
ignorieren läßt ist unverändert geblieben. Ähnlich wie beim I<quoting> gibt es
auch hier für die ordentlichen Schönschreiber mit C<:global> und C<:ignorecase>
eine lange Version dieser Optionsnamen. Neu ist das Adverb C<:ii> aka C<:samecase>
welches ebenfalls die Groß/Kleinschreibung des Suchausdrucks nicht genau nimmt,
ihn aber dann an die Schreibweise des Ersetzten anpasst.

my $text = "Ale Vögel sind schon da, ale ...";
$text ~~ s:g:ii/ale/alle/;
# $text eq "Alle Vögel sind schon da, alle ..."

In ähnlicher Weise gibt es nun auch C<:a> aka C<:ignoreaccent>, sowie C<:aa> aka
C<:sameaccent> das Buchstaben als gleich erkennen und ersetzen können, egal ob
sie einen Punkt, einen Strich, ein Dach oder nichts über sich haben. Dazu ignoriert
der Interpreter alle "I<mark characters>" zusammengesetzter I<Unicode>-Zeichen.

Es kamen etliche weitere Adverbien hinzu aber es wurden auch viele abgeschafft.
Neben den bereits erwähnten C<:e> und C<:ee> betrifft das auch C<:m> und C<:s>.
C<^> und C<$> bedeuten nun immer Anfang und Ende des Strings.
C<^^> und C<$$> stehen für Anfang und Ende einer Zeile: Perl verwendet natürlich
das eigene C<\n> um Zeilen zu unterscheiden, was immer erfolgreich sein sollte,
egal auf welchem Betriebssystem das Programm gerade läuft oder woher die Daten
kommen. Ein C<.> bedeutet jetzt wirklich "ein beliebiges Zeichen". Die alte
Bedeutung (ein beliebiges Zeichen außer \n) nennt sich jetzt konsequenterweise C<\N>,
da großgeschriebene Symbole für Zeichenklassen immer das Komplementär zu ihrem
kleingeschriebenen Pendanten sind.

Auch die Option C<:x> ist weg, da Leerzeichen und Kommentare nun standardmäßig
eingefügt werden können, soweit keine besondere Regel etwas anderes vorschreibt.
Der Syntax von Kommentaren ist innerhalb der Regex der Selbe wie außerhalb. Eine
Raute kommentiert bis zum Ende der Zeile, folgt der Raute beliebige Klammersymbole,
gilt bis zum Schließen dieser Klammern der Inhalt der Regex als Kommentar.

Eine der Regeln die Leerzeichen besondere Bedeutung geben können ist C<:s> aka
C<:sigspace>. Wird sie gewählt, zählt jedes Leerzeichen als "<.ws>", was grob
mit C<\s+> (mehrere Leerzeichen verschiedenster Art) übersetzt werden kann.
Diese I<sigspace> findet Larry so bedeutend, daß C<m:s/.../> gleichbedeutend mit
C<mm//> ist. Ein C<ss/.../.../> entspricht allerdings einem C<s:ss/.../.../>.
C<:ss> aka C<:samespace> unterscheidet sich von C<:sigspace> nur insoweit, daß
auch zwischen dem zweiten und dritten Trenner jedes Leerzeichen als "<.ws>" gilt.

my $lied = "Der Mond\nist\taufgegangen ...";
ss/Mond ist aufgegangen/Plumssack geht im/;
# "Der Plumssack\ngeht\tim".

Sehr praktisch finde ich auch die Optionen C<:c> aka C<:continue> und C<:p> aka
C<:pos>. Ersteres sucht ab einer Position, zweiteres nur an einer Position. Wird
keine Zahl für die Position angegeben, so wird diese C<$/.> entnommen.

my $text = "Vom Eise befreit sind Strom und Bäche."
if $text ~~ m:c(3)/Eis/ {
# wird ausgeführt
...
if $text ~~ m:p(3)/Eis/ {
# wird nicht ausgeführt
...

Was genau gezählt werden soll, kann mit den Adverbien C<:bytes>, C<:codes>
(I<Unicode codepoints>), C<:graphs> und C<:chars> angegeben werden.

Soll eine Ersetzung nur dreimal ausgeführt werden so hilft C<:3x> aka C<:x(3)>
und falls nur der fünfte Fund einer Suche interessiert, so steht dazu C<:5th>
aka C<:nth(5)> bereit. C<:1st>, C<:2nd> und C<:3rd> sind Sonderformen die eine
natürliche Ausdrucksweise erlauben sollen und auch I<Junctions> wie C<nth(3|5|7)>
sind möglich. Die Bedeutung runder Klammern hat sich nicht verändert. Sie
umschließen nach wie vor Teilausdrücke, deren Fund bitte gespeichert werden soll.
Teilausdrücke deren Fund nicht interessiert werden jetzt in eckige Klammern
gesetzt anstatt in C<(?: ... )>.

s:3rd/(\d+)/@data[$0]/;
# ist das selbe wie:
m/(\d+)/ && m:c/(\d+)/ && s:c/(\d+)/@data[$0]/;

Die Regeln für die Nummerierung von C<$0> bis C<$9> haben sich auch geändert, da
bisher kleine Änderungen in der Regex manchmal zu viele Nachbesserungen in dem
Code forderte, der diese Variablen auswertet, da eine ungünstig gesetzte Klammer
die Position aller anderen verändert. Sind Klammern hintereinander wie in C<()()>,
werden sie immer noch genauso gezählt. In einem Fall wie C<(... (...) )> wäre der
Fund der inneren Klammer in C<$0[0]>.

Was mich ebenfalls begeisterte sind die die neuen Optionen C<:ov> aka C<:overlap>
und C<:ex> aka C<:exhaustive> die am besten an einem Beispiel verstanden werden.

$str = "abracadabra";

if $str ~~ m:overlap/ a (.) a / {
@substrings = @@();
# bracadabr cadabr dabr br
}
if $str ~~ m:exhaustive/ a (.?) a / {
say "@()";
# br brac bracad bracadabr c cad cadabr d dabr br
}

Da C<$0> bis C<$9> oft nicht reichen, gibt es C<@()>, der alle Funde enthält.
Um das Suchverhalten zu steuern, kann auch die neue Option C<:ratchet> hilfreich
sein, die jegliches I<Backtracking> verhindert. Das bedeutet, daß Perl den zu
durchsuchenden String nur ein einziges mal von links nach rechts überprüft und
dabei niemals rückwärts geht, selbst wenn in der Regex Alternativen formuliert
sind die erfolgreich sein könnten wenn die Suche noch einmal begonnen würde.
Das I<Backtracking> kann aber auch für einzelne Teilausdrücke abgestellt werden,
indem ihm ein C<:> nachgestellt wird (ehemals "(?> ...)").

=head2 Quanto costa?

Die sogenannten I<quantifier> haben sich kaum verändert. C<?> steht immer noch
dafür den Teilausdruck nicht oder nur einmal zu finden, C<+> für einmal oder
beliebig oft und C<> für keinmal oder beliebig oft. Auch die nicht gierigen
I<quantifier> (C<??,
?, +?>) haben sich in Syntax und Semantik (Bedeutung) nicht
verändert. Nur exakte I<quantifier> werden anders geschrieben, da geschweifte
klammern wie beschrieben immer Blöcke darstellen. Die neue Schreibweise, der
aufmerksame Leser ahnt es bereits, entstammt dem allgemeinem Perlsyntax. Wenn ein
Multiplikationszeichen Wiederholungen bezeichnet, dann kann eine vierfache
Wiederholung ja nur C<**4> lauten. Und darf sich ein Teilausdruck nur ein bis
dreimal wiederholen so wird dies C<()**1..3> geschrieben.

=head2 Nichts ist illegal

Mit Perl 6 gibt sich Larry auch große Mühe Zweideutigkeiten zu vermeiden. Ein
simples:

$text ~~ m/ /;

wirft jetzt einen wunderschönen Error in die Kommandozeile. Um die letztbenutze
Regex wiederzuverwenden schreibe man:

$text ~~ m/ <prior> /;

Oder falls tatsächlich nichts gesucht wird, gibt es die Schreibweisen C<''> und
"<?>". Wird eine Alternative beim Programmieren vergessen wird dies nun sofort
sichtbar. Ja auch das C<|> hat sich in seiner Bedeutung nicht verändert.

$text ~~ m/ |_+ /;

Diese Regex sucht Wörter die aus Buchstaben und Unterstrichen bestehen.
Was vorher Dach war (Komplementärmenge an Zeichen) ist nun Minus. Oder anschaulich:
Keine hexadezimale Zahlen, die man früher mit der Gruppe C<^0-9a-f> erfasst hat,
werden jetzt mit "<-0-9a-f>" gesucht. In diesem Falle würde es "<-hexdigit>"
allerdings auch tun.

=head2 Wendehälse

Vorschau (I<lookahead>) und Rückschau (I<lookbehind>) haben ihre Schreibweise
geändert und sind nun lesbarer. Angelehnt an logische Operatoren wie den Ternären
bezeichnet ein C<?> immer eine positive und C<!> eine negative Option. Folglich
wurde C<(?= ...)> zu <?before ...> und C<(?! ...)> zu <!before ...>. Entsprechend
änderte sich C<(?<= ...)> zu <?after ...> und C<(?<! ...)> zu <!after ...>. Mit
solchen Teilausdrücken verlangt man welche Zeichen vor oder hinter dem gesuchten
Teilstring gewünscht oder unerwünscht sind. Eine detaillierte Erklärung dazu hat
das perlretut in den I<perldocs>.

Wer spätestens jetzt bei all den Neuerungen und Änderungen die Lust verliert,
kann mit C<:P5> aka C<:Perl5> die alten Regeln wieder einsetzen. Jedoch müßen
Optionen auf jeden Fall vor die Regex oder in sie hinein in eine C<(?m ...)> Gruppe.

=head2 Die große Perpektive

Auch wenn mit dem neuen Syntax viele Details klarer werden, können Regex immer
noch schnell unleserlich werden, wenn komplexe Zusammenhänge eingelesen und
aufgespalten werden sollen. (Wir bauen mal schnell einen HTML-Parser.) Hierfür
bräuchte es Mittel wie lex und yacc, die ein Stufenweises aufspalten erlauben.
Die dazu verwendeten Regeln wären verständlicher und einfacher wiederverwendbar.
Genau diese Überlegungen stehen hinter den Perl 6-Grammatiken. Eine C<grammar>
ist in Wirklichkeit eine Klasse, deren Methoden Regex sind. Deshalb behandelt
dieses Tutorial auch die Regex nach der OOP, obwohl Strings ein elementares
Thema für Perlprogrammierer sind. Für C<grammar> wurden 3 spezielle Typen von
Routinen geschaffen. Dies wäre zum einen C<regex>. Damit wird klar warum die
Schreibweise C<regex name {}> geschweifte Klammern verlangt. Eine C<sub> wird
ebenfalls nur mit geschweiften Klammern geschrieben. Um das Parsen einfacherer
Regeln effektiver und das Kombinieren von Teilregeln optisch zu vereinfachen gibt
es noch den Routinentyp C<rule>. Der entspricht C<regex :ratchet :sigspace {...}>.
Der dritte Typ (C<token>) wurde geschaffen um Grundbausteine zu definieren und
entspricht C<regex :ratchet { ... }>. Ein Grammatik um eine Fließkommazahl zu
zerlegen sähe ungefähr so aus:

grammar Float {
rule digits { \d+ }
rule sign { \+ | - }
rule exp :i { e <sign>? <digits> }
rule man { \. <digits> | <digits> [ \. <digits>? ]? }
rule top { <sign>? <man> <exp>? }
}

Einzelne C<rules> definieren Teilausdrücke die spätere C<rules> verwenden, indem
sie den Namen der C<rule> in spitze Klammern setzen. Spitze Klammern und Punkt
markieren Teilausdrücke deren Fund nicht gespeichert wird. (wie das erwähnte <.ws>)
Diese C<rules> sind tatsächlich Routinen denn ich kann sie wie welche aufrufen.

my $result = Float.top("-6.02e+23");

In C<$/{'man'}> oder C<$result{'man'}> steht nun z.B. "6.02", in C<$/{'exp'}{'sign'}>
ein Pluszeichen, da dies das Vorzeichen des Exponenten ist. Aus Folge vier wissen
wir das Hashschlüssel auch einfacher geschrieben werden können, z.B. "$/<sign>".
Nun kann man erkennen wieviel Sinn es macht, benannte Teilausdrücke in spitze
Klammern zu setzen, da die Hashschlüssel unter denen ihr Fund gespeichert ist,
gleich aussehen. Ähnliches war innerhalb einer Regex ab Perl 5.10 nur mit
"(?<name> ...)" erreichbar. Grammatiken sind allerdings wie (andere Klassen auch)
ableitbar und können so einfach auf bereits bekannte Art kombiniert und erweitert
werden. Dies ist nicht nur praktisch sondern erlaubt erst viele besondere
Fähigkeiten von Perl 6, daß damit immer mehr zu einer Meta-Programmiersprache
mutiert. Metaprogrammierung wird jedoch Thema der nächsten und letzten Folge sein.


Upload Files

Click "Browse" to find the file you want to upload. When you click "Upload file" your file will be uploaded and added to the list of attachments for this page.

Maximum file size: 50MB

 
 
 
File Name Author Date Uploaded Size

Save Page As

Enter a meaningful and distinctive title for your page.

Page Title:

Tip: You'll be able to find this page later by using the title you choose.

Page Already Exists

There is already a page named XXX. Would you like to:

Save with a different name:

Save the page with the name "XXX"

Append your text to the bottom of the existing page named: "XXX"

Upload Files

Click "Browse" to find the file you want to upload. When you click "Add file" this file will be added to the list of attachments for this page, and uploaded when you save the page.

 
 
 
Add Tags

Enter a tag and click "Add tag". The tag will be saved when you save the page.

Tag: 

Suggestions: