Quick jump:  What's new

 
Go to:
 
Weblog: Tutorial 
in Perl 6
Perl 6 Tutorial Part 1


Motivation for a friendly Perl 6 tutorial

Perl 6 appears slowly before the wondering eyes of the curious, but if they grab Rakudo and the specification (aka Synopses) and try to write their first program, they often don't get too far. The language changes a bit every week and Rakudo doesn't support every command. And synopses aren't an easy read either. They are huge and contain a lot of nifty words. More written to clear edge cases to computer science students than to help the noobs to get a grasp. Therefore we need a tutorial that introduces the reader slowly into the world of Perl 6 and requires as little knowledge as possible. Of course some basic programming skills are needed and some Perl 5 wisdom is helpful, but everything else will be explained on the way. Every chapter of this tutorial is focused on one topic and contains many examples, so you can start to play with the language, which I encourage you to do. A start is made quickly, but it takes time to really learn Perl, and the new generation is optimized even on that.

Greetings Perl 6

Nearly every introduction starts with a "Hello World" example. I tried to come up with something more inventive, but its still the smallest, simplest program that does something joyfully useful. So shall we start with something worn out? No, something well-tried:

v6;
say "Greetings, earthlings, err Perl 6 neophytes.";

This first program outputs the expected text. The command say corresponds to the familiar print, but appends a line ending appropriate to the current operating system (e.g. a newline under Unix, a carriage return and newline under Windows). The print command is still available, but the new idiom is to use say.

I can already hear the objections of the more experienced Perl monger: A good script begins with strict and warnings. And right they are. In Perl 5 the program should read:

Perl 5:

use strict;
use warnings;

print "Greetings, earthlings, err Perl 6 neophytes.\n";

But in Perl 6 there is no need, because both pragmas are now enabled by default. This saves not only the two lines of code, which are in almost all of my programs, but it helps those who know nothing about the pragmas become aware of potential problems sooner. If necessary, one or both may be disabled with no strict or no warnings.

Parentheses and Curly Braces

To put more life into our program, lets use a variable, to which we assign a value and then reference:

my $a = 3;
say "I juggle with $a balls.";

We see that scalars still contain a single value (of any data type) and still use the sigil $. There's nothing more to explain. The variable is declared with my in the familiar lexical local scope, since use strict is in effect. And the double quote works basically as in Perl 5, interpolating control chars and variables. Perhaps playing with one or two balls shouldn't be called juggling:

my $a = prompt "Some spare balls? ";
if $a > 2 { say "I juggle with $a balls." }

The promt is a handy shortcut that replaces 3 commands. It does say, reads from STDIN (now $*IN) and chomps (cutting trailing \n) the result. In Perl 6 all reading from a data stream gets autochomped. The space after "balls? " is left by intention so that the input doesn't clutch on the question output.

And also in the second line we see a pleasant difference from Perl 5. The term following the 'if' may, or may not be placed in parentheses. Parentheses are used in Perl 6 only for grouping. If we have no nested structure, but only a single statement or a simple sequence of statements, they are not needed. This is true everywhere. In a similar way, an array, a variable that stores multiple values, can now be fed without parentheses.

Perl 6:

my @primes = 2,3,5,7;

This is possible because the comma is a list (creating) operator now. The second, not quite so obvious new feature in Perl 6, which is shown in the 'if' statement above, is the absence of a final semicolon. Since Perl 1.0, the semicolon separates individual commands. Only the last command within curly brackets (a block), does not have to end with a semicolon. Also you can see (above) you no longer have to remember when a semicolon is following a closing brace. (These rules were largely taken from C.) Larry has decided that each semicolon after braces is from now on optional. For 'if' blocks the same is true in Perl 5, but not as in 'eval' blocks.

Perl 5:

eval { print "Find all 3 differences !\n" };

Perl 6:

try { say "Find all 3 differences!" }

The third difference is the 'try' command, because 'eval' accepts only strings as parameter. Use now 'try' if you need a trial execution of blocks where errors can't stop your program, but where you can analyze the during runtime.

A not so free format

In terms of formatting, Perl has become somewhat more stringent. Leading spaces cannot be inserted anywhere at will. Of course, the width of indentation, and in general the number of spaces and line breaks between two parts of an expression are irrelevant.

Perl 6:

$b = $a ; # assign a value
$c = $a; # now I still have a copy of the value
$d
=
$a
; # And even a

But spaces and line breaks are already a divisive issue for operators, you must now pay more attention. Especially the new prefix Operator ('='), which takes the part of the popular perl5 diamond operator ('<>'), and can easily be mistaken for an assignment. The following two instructions can cause confusion:

Perl 6: Perl 6:

$b = =$a; # assigning a line from a stream
$b == $a; # numeric comparison

The same character can now display multiple operators, depending on how it is surrounded by spaces:

Perl 6:

++$b # Präfix-Operator, Autoinkrement vor Evaluierung
$b++ # Postfix-Operator, Autoinkrement nach Evaluierung
+$a # Präfix-Operator, das selbe wie num $a, ähnlich int $a
$a + $b; # Infix-Operator, Addition
=$b # Präfix-Operator, Zeile aus einem Datenstrom
$a = $b; # Infix-Operator, Zuweisung
$a # Circumfix-Operator, Teilarray oder Reduktionsoperator
< $a> # Circumfix-Operator, qw()
<< $a >> # Circumfix-Operator, interpolierendes qw()

Diese Unterscheidungen sind nicht gänzlich neu, da beide Arten des Autoinkrementes
schon in C bekannt sind. Unerwartete Probleme kann es für Umsteiger aber auch beim
Schreiben von Arrays, Hashes und von Objekten geben. Folgende Schreibweisen werden
nicht mehr erkannt:

Perl 5:

$array $i;
$hash {$k};
$obj -> methode ();

Wer seinen Quellcode so formatieren möchte, daß bestimmte Teilausdrücke oder die
Zeilenenden genau untereinander stehen, braucht in Perl 6 die "long dot" genannte
Schreibweise:

Perl 6:

@array\ .$i; # ein Wert aus einem Array
%hash\ .{ $k }; # ein Wert aus einem Hash
$obj\ # ich kann auch Kommentare einfügen
.methode ( ) ; # manche Leerzeichen sind erlaubt

Blitzmerker haben aus diesem Code bereits geschlossen, daß Arrays nun immer mit
'@' anfangen, Hashes nun immer mit '%' und das der Punkt das Objekt und den Namen
der Methode trennt, wozu früher ein Pfeil ('->') diente (z.B.:'$obj.methode()').
Daß der Backslash ('\') die nachfolgenden Leerzeichen und Zeilenumbrüche "quotet",
also ihrer normalen Bedeutung enthebt, ist auch nichts wirklich Ungewöhnliches.
Das Stirnrunzeln beim Betrachter dieser Zeilen erwarte ich erst, sobald sein Hirn
diese beiden Gedanken verbindet und die Frage auftaucht: Der Sliceoperator (die
Klammern hinter einer Variable, mit deren Hilfe wir einzelne Werte, also Teile
(slices), eines Arrays oder Hashes geliefert bekommen) ist doch nicht ernsthaft
eine Methode des Objektes vom Typ Array oder Hash? Doch, ist es. Perl 6 ist so
objektorientiert wie Ruby, auch wenn es sein möglichstes tut, daß vor dem Benutzer
zu verbergen. Zum Beispiel dadurch, in dem es oftmals den Punkt optional läßt.
Statt dem auch möglichen '@array.3' reicht wie gewohnt ein '@array[3]'. Nur in
diesem Sonderfall, wenn man zwischen Objekt und Methode Leerzeichen einfügen will,
muß der Punkt verwendet werden, um Doppeldeutigkeiten zu vermeiden. Der Backslash darf auch nicht fehlen, denn ein '.say' ist in Perl 6 eine andere Schreibweise für '$.say' oder auch 'say $'. Steht also ein Punkt vor einem Befehl, sucht Perl kein vorher erwähntes Objekt, dessen Methode hier aufgerufen werden soll, sondern interpretiert diesen Befehl als Methode der aktuellen Kontextvariable, die immernoch, wie in Perl 5, '$_' heißt.

noch Kommentare?

In allen bisherigen Beispielen war es zu sehen: Wie aus Perl 5, anderen Sprachen
und Unix-Shells bekannt, leitet auch in Perl 6 die Raute ('#') einen Kommentar
ein, der mit der selben Zeile endet. Neu in Perl sind jedoch Kommentare, deren
Ende sich bestimmen läßt. Diese können mitten in eine Zeile eingefügt werden oder
sich über mehrere Zeilen erstrecken. Solche Kommentare beginnen auch mit einer
Raute, der jedoch unmittelbar eine Klammer folgt. Als Klammern gelten die Zeichen
'()', '[]','{}' und '<>', wobei natürlich die zugehörige schließende Klammer den
Kommentar beendet. Dabei kann man stets die Klammern wählen, die sich optisch vom
Quellcode abheben. Dies kennt man in Perl 5 schon von den Regulären Ausdrücken
und vom Quoting, aber zur Verdeutlichung einige Beispiele:

Perl 6:

my $a #( = 6; ) = 5; # es 5 wird zugewiesen
say "$a #(????) $a"; #( Ausgabe von '5 #(????) 5'
keine Kommentare innerhalb von Strings )

$affe# .schreit().guckt() # der schreit nicht, der guckt nur
@zahlen#({ ?? ).reverse; reingefallen alles noch Kommentar
}).sort; # gibt sortierte Version des Arrays zurück

Die letzten beiden Beispiele zeigen, daß mehrere Klammern benutzt werden dürfen.
Dabei hat man nur darauf zu achten, beim Abschließen des Kommentars genau die
passende Art, Anzahl und Reihenfolge der Klammern zu verwenden. Manche mögen darin
wieder neue Wege sehen, unleserlichen Quelltext zu schreiben, aber hinter dem
Vorgestelltem steht vor allem eine neue Arbeitslogik des Perlinterpreters, die
einige aufdringliche und einige leicht zu übersehende Problemfälle bereinigt.

Setz es in "Anführungszeichen"

Mit der Arbeitslogik meine ich das "one-pass parsing", zu deutsch: ein Interpreter
kann in einem Durchgang den Code vollständig erfassen. Mit Perl 5 ist das leider
anders. Hier schaut der aufgebohrte Bison-Parser öfters vor und zurück, um zu
erkennen, was der Programmierer grad von ihm möchte und wechselt dabei zwischen
verschiedenen Zuständen, wie etwa: "Dies ist Kommentar", oder "Dies ist gerade
eine Anweisung". Perl 6 kennt nicht die Sonderregeln und Doppeldeutigkeiten,
welche dies notwendig machten. Das macht die Interpreter nicht nur schlanker und
schneller, sondern auch narrensicherer. Beim Einlesen kann ein Perl 6-Interpreter
die Quellen in immer kleinere Bedeutungseinheiten aufspalten. Selbst wenn eine
dieser Bedeutungseinheiten keinen Sinn ergibt (eine Katze über die Tastatur lief),
beeinträchtigt dies nicht den Rest des Programmes. Ein Perl 5-Beispiel mit einer
Regex, die mit Kommentaren versehen ist zeigt, welche hinterhältigen Fehler die
alte Lesart bisher erlaubte:

Perl 5:

/ ( # begin der capture
\w+ # Buchstaben und / oder Zahlen
| # nächste Alternative
...
) /x;

Der Slash ('/') im Kommentar läßt perl vermuten, hier sei die Regex zu Ende und
wird vom Nachfolgendem sehr sehr verwirrt. In Perl 6 haben Kommentare keinen
Einfluß mehr auf Anweisungen und auch Dokumentation (POD) kann überall eingefügt
werden, ohne daß es mißverstanden wird. Auch öffnende Klammern, denen ihr Gegenstück
fehlt, werden in Perl nun wohlwollend übergangen. Allerdings ist im täglichen
Gebrauch vor allem folgende Konsequenz dieser Technik wirklich praktisch:

Perl 6:

say "Und der Gewinner ist %gewinner{"name"}.";

Mit Perl 5 ginge das nicht ansatzweise, weil perl die zweiten Anführungsstriche
als Textende ansieht, als Ende des Zustandes: "Ich lese gerade einen Text von
links nach rechts.". In Perl 6 werden jedoch zuerst die äußeren Anführungsstriche
als Begrenzung des auszugebenden Textes erkannt und danach die darin zu interpolierende
Hashvariable %gewinner, von der nur der Wert mit dem Schlüssel "name" gebraucht
wird.

Dies soll für einen ersten Rundgang genügen, durch die allgemeinen Grundlagen von
Perl 6. In der nächsten Folge werden Operatoren zur Veränderung und dem Vergleich
von Skalarwerten das Hauptthema sein.


Overview | Next Chapter

permalink
Perl 6 Tutorial Part 7


Text, Rules and Grammars

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.

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.

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.

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.

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

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.

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 "(?> ...)").

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.

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.

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.

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.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial Part 6


Objects and Roles

Willkommen und hereinspaziert mein Damen und Herren. Hier erleben sie Menschen -
Tiere - Sensationen, so etwas hat die Welt noch nicht gesehen. Und fürwahr das
Objektsystem von Perl 6 übertrifft in seiner Ausgereiftheit und Vielseitigkeit
alles bisher aus der Praxis Bekannte. Hier gab es einige der stärksten Änderungen
gegenüber Perl 5. Vieles davon ist jedoch heute bereits (leicht abgeändert) mit
Moose umsetzbar.

Das alte System

Die alte OOP bestach vor allem durch ihre Freiheit und daß sie mit minimalstem
syntaktischen Aufwand in das objektlose Perl 4 eingefügt wurde. Nur C<bless> (der
Befehl zur Erzeugung eines Perl 5-Objektes) kam extra deshalb dazu. Und dieser
Befehl bindet "lediglich" eine Referenz - meist eine Hashreferenz - an ein C<package>
und macht sie so zum Objekt. Damian Conway bewundert diesen "Taschenspielertrick",
und zeigt in seinem Buch I<"Object Oriented Perl">" die immensen Möglichkeiten des
"Tricks" auf. Larry hatte diesen "Trick" eigentlich von Python abgeschaut, es
aber seitdem bereut. Nicht nur Guido van Rossum hat mit der kürzlich erschienenen
Version 3 diese Art der OOP aus Python gestrichen, auch Perlkreise sehen dieses
Objektsystem derzeit als größte Schwachstelle der Perlsyntax. Es ist nicht nur für
viele Perlneulinge eine echte Hürde, sondern verstößt auch gegen Perls eigene Regeln.

Was Perl ausmacht, zeigt das alltägliche Grundbeispiel, das gleichzeitig den ersten
Babyschritten jedes Programmierers entspricht: C<print "Ich kann schon laufen!\n">.
Keine Systembibliothek wird importiert, keine C<main>-Routine ist notwendig, keine
Klasse muss drumherum gestrickt werden. Dem Programmierer wird weder Arbeit noch
Wissen abverlangt, das nicht unbedingt notwendig ist. In Perl 6 und 5.10 reicht
sogar C<say "Ich kann schon laufen!">, da die Bedeutung von Steuerzeichen sicher
nicht das Erste ist, was Anfänger lernen sollten. Wenn er jedoch eine einfache
Klasse schreiben will, braucht er einige Kenntnis von Namensräumen, Referenzen,
Hashes, C<bless> und Perls eigenwilliger Art der Parameterübergabe, wie es das
folgende Beispiel zeigt:

Perl 5:

package Heart::Gold;

sub new {
bless {speed => 0 }, shift;
}

sub speed {
my $self = shift;
my $speed = shift;
if ($speed) { $self->{speed} = $speed }
else { $self->{speed} }
}

sub stop {
my $self = shift;
$self->{speed} = 0;
}

Benutzt wird diese Klasse wie folgt:

Perl 5:

my $ufo = Heart::Gold->new();
# "gib Gas, Scotty"
$ufo->speed('7c');
# "alle Maschinen halt"
$ufo->stop();

Für gute Perl-Programmierer ist das kein Problem, aber sie freuen sich über Wege,
das Gleiche mit weniger Tippaufwand zu erreichen, wie die Existenz von I<Moose>,
I<Class::Accessor::Fast> und mindestens einem Dutzend weiterer Module beweist.
Tipparbeit zu reduzieren ist ebenfalls ein wichtiges Perlziel.

... und nun das Neue

Eine fast funktionsgleiche Klasse sähe in Perl 6 so aus:

Perl 6:

class Heart::Gold {
has $.speed;
method stop { $speed = 0 }
}

Das ist kurz, klar und weniger fehleranfällig. Bereits der erste Befehl kündigt
unmissverständlich an, daß hier eine Klasse erzeugt wird (deklarativer Syntax).
C<class> ist (genau genommen) ein I<Blockmodifikator>, der dem nachfolgendem
(in geschweiften Klammern stehenden) lexikalischen Bereich einen Namen zuweist
(wie C<package>) und daraus ein Protoobjekt erzeugt aus dem nachfolgend Objekte
geklont werden können. Perl 6 hat eine prototyp-basierte OOP, ähnlich Javascript.
Möglich ist auch die Schreibweise C<class Heart::Gold;> ohne geschweifte Klammern.
Dann nimmt der Interpreter jedoch an, daß alles bis zum Dateiende zu jener Klasse
gehört.

Der zweite Befehl (C<has>) definiert ein Attribut der Klasse. Attribute sind die
Daten die zu jedem Objekt gehören und die von außen unsichtbar sind. (Das wäre in
Perl 5 nicht einfach möglich.) Auf manche dieser Daten ist aber der Zugriff von
außen gewünscht. In vielen Sprachen werden deshalb Methoden geschrieben die Werte
von und zu den Attributen weiterleiten. In Perl 6 kann der Programmierer mit der
Twigil (dem Punkt nach "$") die öffentlichen Attribute markieren und die Methode
gleichen Namens, die einen Zugriff von außen erlaubt, wird automatisch erzeugt.
Möchte man neben dem "Getter" auch einen "Setter" (Methode, die dem Attribut
einen Wert zuweist), muss man das Attribut mit dem Trait (Eigenschaft die während
der Kompilierung unabänderlich festgelegt wird - zu deutsch: Charakterzug) C<rw>
versehen.

has $.speed is rw;

C<rw> steht für "read/write" (lesen/schreiben) und wird zu vielen Gelegenheiten
angewendet, z.B. wenn eine Datei für Lese- und Schreibzugriff geöffnet wird.
Der erzeugte Setter ist eine Lvalue-Routine. Ihm kann also zugewiesen werden.
Das erlaubt nicht nur einen einfachen, intuitiven Umgang, da Getter und Setter
sich wie Variablen verhalten, es vermeidet auch hinterhältige Fallen, die eine
selbstgeschriebene, kombinierte Getter/Setter-Methode aufstellt. Sie sind auch
angenehmer im Umgang als die Getter/Setter, die in unserem Beispiel "GetSpeed"
und "SetSpeed" oder "get_speed" / "set_speed" heißen würden. Sind alle oder die
Mehrheit der Attribut "rw", ist es kürzer zu schreiben:

class Heart::Gold is rw { ...

Soll ein Attribut privat sein, wird es mit "!" (Punkt mit Wand darüber) markiert.
Der automatisch erzeugte Getter ist dann eine private Methode, die nur innerhalb
der Klasse benutzt werden kann. Ohne Twigil wird keine Zugriffsmethode erzeugt.
Das ist auch nicht notwendig, da jedes Attribut innerhalb der Klasse einer
lexikalisch lokalen Variable entspricht. Deshalb kann es auch kein gleichnamiges
privates und öffentliches Attribut geben. (Innerhalb der Beispielklasse greift
C<$.speed>, C<$!speed> und C<$speed> auf das gleiche Attribut zu.) Eine lokale
Klassenvariable (kein Attribut) is jedoch möglich, wenn auch nicht empfehlenswert,
es der Autor wünscht sich eine Variable die alle Instanzen der Klasse teilen.
Dies hieße ebenfalls C<$speed>, während das Attribut weiterhin mit C<$!speed>
in der Klasse erreichbar wäre, in der es definiert wurde. Die Befehle C<my> und
C<our> haben bei I<Accessoren> (Getter/Setter) keinen Effekt.

Der dritte neue Befehl ist C<method>, der selbsterklärend (anstatt C<sub>) eine
Methode erzeugt (deklarativer Syntax). Methoden sind Routinen eines Objektes.
Objekte wurde ja "erfunden" um Daten mit zugehörigen Routinen zusammenzufassen.
Sie werden nun mit C<$objekt.methode()> anstatt C<$objekt->methode()> aufgerufen.
Das spart ein Zeichen zu tippen, liest sich leichter und ist verbreiteter Standard.
Mit diesem Wissen wird klar, warum C<$.speed> der Befehl ist, eine gleichnamige
Methode zu erzeugen.

Verwendung von Objekten

Ein weiterer großer Unterschied zum Perl 5-Beispiel ist das Fehlen der Methode
C<new>. Die wird automatisch erzeugt, da der Interpreter auf alle nötigen
Informationen zugreifen kann. Bei Bedarf kann allerdings eine eigene C<new>-Methode
oder ebenso eigene Getter/Setter erstellt werden, welche die autogenerierten
Methoden überschreiben. Die Verwendung einer Klasse sieht damit so aus:

my $ufo = Heart::Gold.new();
$ufo.speed ='5c';
$ufo.stop;
# 0, da numerischer Kontext erzwungen wird
say + $ufo.speed;

Die ersten beiden Schritte ließen sich sogar noch zusammenfassen:

my $ufo = Heart::Gold.new( speed =>'5c'); #oder
my Heart::Gold $ufo .= new( speed =>'5c');

Praktischerweise kann die autogenerierte C<new>-Methode auch Attribute befüllen.
Im zweiten Fall wird der Variable die Klasse (Protoobjekt) als Datentyp zugewiesen.
Gemäß der Logik selbszuweisender Operatoren (z.B."+=") wird .new auf den Aufrufer
angewendet und so entsteht ebenfalls ein Objekt. Diese Art der "selbstzuweisenden"
Methoden wird I<Mutator> genannt (z.B. auch C<@array .= sort>). Auf diese Weise
kann man ebenso Objekte durch Klonen erzeugen:

# mit alternative Paar-Konstruktor
my $arv = $ufo.clone( :speed<2c> );

Natürlich kann ein ganzer C<%Hash> an Werten bei der Erzeugung übergeben werden.
Dabei sieht die Syntax etwas wie Perl 5 aus, da es immer noch eine Methode namens
C<bless> gibt, die ähnlich dem altem C<bless>-Befehl funktioniert.

my $klasse = class Heart::Gold { ... }
my %werte = :speed<20c>;
$efv = $klasse.bless( %werte );

Eine Kleinigkeit fehlt aber noch: Wie erklärt man eine Methode C<protected>, also
nicht über ein Objekt aufrufbar? (C<$objekt.methode()>) Genau wie man seit Perl 5
Variablen auch in einem Namensraum von außen abschottet, mit C<my>.

class Heart::Gold {
my method secret_maneuver { ... }
}
my $ufo = Heart::Gold.new;
# Laufzeiterror:
$ufo.secret_maneuver;

Mit C<my> lassen sich auch lexikalisch lokale Klassen deklarieren. Die benötigt
man zum Beispiel, wenn Attribute selber Objekte sind, deren Klasse geheim bleiben
soll. In diesem Beispiel:

class Heart::Gold {
class Movement { ... }
has int $.length where {$_ >= 0};
has Movement $drive;
my method secret_maneuver { ... }
}

hat ein Attribut einen vor Ort erstellten Subtyp (siehe letzte Folge), das andere
ist ein Objekt (Instanz der Klasse C<Movement>). Da diese Klasse nicht mit C<my>
als lexikalisch lokal deklariert wurde, ließen sich außerhalb von C<Heart::Gold>
ebenso C<Movement>-Objekte erstellen. Auch prozeduraler Zugriff auf den Namensraum
C<Movement> wäre dann möglich.

Delegier die Arbeit

Eine Klasse C<Movement> ist sinnvoll, da solch ein fortschrittliches Fluggerät,
sicher sehr viele Eigenschaften (Attribute) und Funktionen (Methoden) besitzt.
Sie alle in eine Klasse zu schreiben ginge zu Lasten der Nachvollziehbarkeit.
Unser Attribut C<$drive>, welches das Antriebsaggregat repräsentiert, ist also
ein Objekt, dessen Methoden nun Informationen über Geschwindigkeit und ähnliches
herausgeben. Es erscheint als müsste nun doch ein I<Getter> C<$.speed> geschrieben
werden, welcher die Methode C<$drive.speed> aufruft und ihr Ergebins weiterleitet.
Aber auch hierfür gibt es eine kurze Schreibweise die genau dies automatisch tut.

class Heart::Gold {
has Movement $drive handles 'speed';

Dieses Konzept wird in der OOP-Welt I<Delegation> genannt und wird natürlich wie
fast alles bekannte auch von Perl 6 angeboten. Nich umsonst heißt das neue Motto:
"all your paradigms are belong to us".

Regelung der Erbschaft

Somit erübrigt sich auch die Frage: "Gibt es Mehrfachvererbung in Perl 6?", doch
beginnen wir mit der Einfachen. Klassen vererben einander all ihre Attribute und
Methoden. Das nennt man auch "ableiten". So kann eine einmal geschriebene Klasse
wiederverwendet und Schritt für Schritt erweitert werden. Besitzt die Elternklasse
dabei eine Methoden, welche der Sprössling auch definiert, so überschreibt er
damit die geerbte Methode. Ich könnte eine einfache Klasse schreiben, die den Flug
von Pollen oder einer Pusteblume wiedergibt. Von dieser könnte eine nächste erben
und einige Methoden zufügen, welche Manöver eines Heißluftballons beschreiben,
u.s.w bis wir in sechster oder siebenter Generation einen Ufoantrieb haben der
auch auf die Zeitverschiebungen bei Interdimensionsreisen eingehen kann.

class UfoDrive is AdvancedRocket {
...
}

Da die "Heart of Gold" bekanntermaßen einen Unwahrscheinlichkeitsantrieb besitzt,
der die unwahrscheinlichen Beträge auf den Rechnungen des integrierten Restaurants
nutzt, muss noch von einem solchen alles vererbt werden.

class UfoDrive is AdvancedRocket is Restaurant { ... }

Das ist eine Kurzschreibweise für:

class UfoDrive {
is AdvancedRocket;
is Restaurant;
...
}

Es sei anzumerken, daß die Klasse I<Restaurant> gleichnamige Methoden der Klasse
I<AdvancedRocket> "überschreibt". In Java können Klassen als "final" markiert
werden. Das ist in Perl 6 nicht direkt vorgesehen, jedoch kann in einem Programm
mit dem Pragma C<use oo :final;> angewiesen werden, alle Klassen automatisch als
"final" zu betrachten. Ausnahmen können mit

{
use class :nonfinal;
class UfoDrive { ... }
class Heart::Gold { ... }
}

oder

class Insect is nonfinal { ... }

angegeben werden. Einzelne Methoden, die nicht vererbt werden sollen, können als
C<submethod> deklariert werden, die ebenfalls mit einem C<my> davor nicht über
ein Objekt oder den Namensraum aufrufbar sind.

Rollen die ich spiele

Eine Rolle wird definiert wie eine Klasse, nur mit dem Schlüsselwort C<role>
anstatt C<class>.

role Blinken {
has @.lichter;
method blinken { ... }
}

Von einer C<role> kann aber kein Objekt erzeugt (geklont) werden. Wozu dient dann
eine C<role>? Die Praxis hat gezeigt, daß mit Klassen allein der Code nicht immer
einfach wiederverwendbar ist. Schnell kam es zu Bäumen von Vererbungshierarchien.
Was aber tun, wollten zwei "Äste eines Baums" wenige Methoden gemeinsam verwenden?
Selbst Sprachen, die Mehrfachvererbung kennen (nicht Java und C#) erzeugen damit
neue Probleme, die mit Rollen vermieden werden können. Sie sind auch syntaktisch
natürlicher und bieten sogar mehr Sicherheit ungewollte Methoden zu überlagern als
Rubys I<Mixins>. Rollen werden während der Kompilierung in Klassen "eingebunden",
wenn es die Klasse fordert.

class Heart::Gold does Blinken {

oder

class Heart::Gold {
does Blinken;
...
}

Wie zu erahnen, besitzt nun auch die Klasse I<Heart::Gold> die Methode I<blinken>.
Roles können jedoch nicht Methoden einer Klasse überlagern. Versuchen zwei Roles
gleichnamige Methoden zu importieren gibt es einen Fehler beim Kompilieren, es
sei denn die Klasse hatte eine gleichnamige Methode und der Konflikt muss nicht
gelöst werden. Perl 6 kennt Wege wie Roles und Klassen ihre Konflikte selbst lösen
können, dies würde aber den Rahmen überdehnen. Was jedoch jeder über Roles wissen
sollte, der ihre Verwendung in Betracht zieht, betrifft Attribute. Roles können
natürlich eigene Attribute mitbringen. Dies tat die Role im Beispiel mit:

has @.lichter; # oder:
has @!lichter; # oder:
my @!lichter; # für Klasse unsichtbar

Sie könnte aber auch Attribute der Klasse mitnutzen. Das meldet sie an mit:

has @lichter;

und der Kompiler prüft ob die Klasse dieses Attribut besitzt. Ein großer Nutzen
der Roles kommt auch von ihrer Fähigkeit sich auch zur Laufzeit einzubinden.

$ufo does Blinken;

Mehrere Roles müssen einzeln gebunden oder geklammert werden:

(($ufo does Blinken) does Tarnen);

Bei diesen Fähigkeiten, gibt es auch Bedarf zur Laufzeit zu prüfen, ob eine Role
in ein Objekt "eingemischt" wurde.

if $ufo.does(Blinken) { ... }

Was in unserem Falle wahr (Bool::True) ist. Ebenso kann geprüft werden ob ein
Objekt von einer Klasse abgeleitet wurde:

if $ufo.isa(Heart::Gold) { ... }

Dieser Syntax ähnelt sehr Perl 5, Folgendes ist jedoch neu, auch wenn Perl 5.10
bereits smartmatch kennt:

if $ufo ~~ Blinken { ... }
if $ufo ~~ Heart::Gold { ... }

Beide Fälle sind genauso wahr, da C<~~> eine große Tabelle hat und je nach Typ der
Parameter versucht das "Richtige" zu tun. Solches Verhalten erreicht man durch
C<multi>-Methoden (in vorigen Folge vorgestellt). Wenn I<Roles> I<Multimethoden>
importieren, dürfen diese gleichnamig sein, solange die Signaturen nicht gleich
sind.

Zeig mir dein Inneres

Objekte lassen sich in Perl 6 weitaus tiefer durchleuchten als es das Smartmatch
tut. C<$ufo.HOW> oder C<^Heart::Gold> erlaubt z.B. den Zugriff auf die Metaklasse
und C<Heart::Gold.^methods()> liefert beispielsweise alle Methoden der Klasse.
Doch an der Stelle möchte ich mich für dieses mal verabschieden, da die Methoden
zur Introspektion (hoffentlich bald vollständig) im Tutorial der Perl-Community.de
Wiki aufgelistet sind und ein Aufzählen einer API langweilig ist. Diese API ist,
im Gegensatz zu dem in dieser Folge vorgestellten Syntax, keinesfalls stabil und
endgültig, sodass ein Blick in den tabellarischen Index B des Tutorials sinnvoll
ist. Die nächste Folge wird behandeln, was Perl einst berühmt und berüchtigt
machte: die regulären Ausdrücke die jetzt wirklich regulär sind, sich nicht mehr
Ausdruck nennen und auch wie Objekte behandelt werden können.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial Part 5


Captures und Subroutinen

Willkommen zum fünften Teil dieses ausführlichen Perl 6-Tutorials, der sich nur
mit dem kleinen "sub"-Befehl beschäftigen wird und einige Dingen die dazu gehören.
Subroutinen oder Funktionen, die in Perl 6 immernoch mit C<sub> deklariert werden,
gehören zu den wichtigsten und grundlegendsten Techniken der Programmierkunst.
Es sind Teilprogramme die mit einem möglichst aussagekräftigen Namen aufgerufen
werden. Meist werden ihnen Werte übergeben und oft liefern sie auch ein Ergebnis
zurück. Jeder der schon etwas Perl kennt, hat bereits Ähnliches geschrieben wie
folgende Funktion, welche die Länge der Hypotenuse berechnet.

sub hypotenuse {
my ($a, $b) = @_;
sqrt( $a**2 + $b**2 );
}

Und die gute Nachricht für Lernfaule lautet: Dies ist vollständig gültiges Perl 6.
Menschen die jedoch gerade von C oder Java zu Perl wechseln, hätten die Subroutine
wohl so geschrieben:

sub hypotenuse ($a, $b) {
return sqrt( $a*$a + $b*$b );
}

Und die gute Nachricht für diese Neulinge lautet: Dies ist ebenfalls vollständig
gültiges Perl 6. Die zweite Lösung liegt näher an dem was die meisten anderen
Sprachen da draußen betreiben (entspricht häufig den Lesegewohnheiten) und sie
spart mühevolle Fingerbewegungen (ebenfalls ein Indikator ob etwas perlish ist).
Aber was genau wurde geändert? Folgen dem C<sub>-Namen runde Klammern, definiert
das eine Signatur (Liste der Parameter) und keine Prototypen mehr wie in Perl 5.
Diese Parameter (auch C<@>) sind keine lokalen Variablen und (wenn vorher nicht
anders deklariert) schreibgeschützt. Folgt keine Signatur, landen Parameter in
C<@>, wie aus Perl 5 gewohnt. TIMTOWTDI.

Geforderte Parameter

Signaturen helfen jedoch nicht nur Neulingen, sondern nehmen Jedem Arbeit ab.
Zum Beispiel kann C<sub hypotenuse> nur dann sinnvolle Ergebnisse erzielen, wenn
sie 2 Parameter bekommt. Im alten System der Parameterübergabe wäre es aufwendig
dies einzufordern. In Perl 6 braucht der Programmierer dafür gar nichts zu tun,
denn würde man die zweite Routine mit C<hypotenuse(5)> oder C<hypotenuse(2,3,4)>
aufrufen, gäb es eine dicke Fehlermeldung zur Kompilierungszeit. Wäre die C<sub>
wie im ersten Beispiel implementiert, würde der Kompiler lautlos schnurren wie
ein Kätzchen, auch wenn die Ergebnisse der Funktion nicht immer brauchbar wären.
Doch wenn schon die Parameter prüfen, dann auch auf den Datentyp. C<Num> ist ein
nativer Datentypen für Skalare und entspricht dem Zahlenbereich rationaler Zahlen.

sub hypotenuse (Num $a, Num $b) {
return sqrt( $a*$a + $b*$b );
}

Der Vorteil einer solchen Prüfung: die Fehlermeldung kommt, wenn Unbeabsichtigtes
passiert (hier bei nichtnumerischen Längenangaben), nicht erst Zeilen später,
wenn ein Folgebefehl versagt. Somit sind Ursachen wesentlich leichter auffindbar.
Und um konsequent zu sein sollte die C<sub hypotenuse> nur Zahlen größer als 0
zulassen, da sie mit physischen Längen rechnet. Dazu definieren wir den eigenen
Datentyp C<Num+> (analog zu Q+) als Untermenge von C<Num> wie folgend:

subset Num+ of Num where { $_ > 0 };

Perfekt wäre es, wenn auch der Typ des Rückgabewertes festgelegt werden könnte.
Das wirkt in diesem Beispiel vielleicht etwas übertrieben, aber Programme werden
so zuverlässiger. Diese Art der Programmierung, bei der der Compiler die Anzahl,
und Typen der Ein- und Ausgabe einer Routine prüft, wird "Design by Contract"
genannt und wurde zuerst durch die Sprache Eiffel bekannt. Da Perl 6 Motto heißt
"All your Paradigm's belong to us" heisst, ist das beschriebene auch hier möglich:

Num+ sub hypotenuse (Num+ $a, Num+ $b) { ... }
sub hypotenuse (Num+ $a, Num+ $b --> Num+) { ... }

Bla Bla Bla

Die "..." in den letzten Beispielen waren nicht nur Dekoration die andeutet,
daß an dieser Stelle der eigentliche Code später eingefügt wird. Auch der Perl 6
versteht es in dem Sinne. Der Interpreter geht davon aus, daß der Programmierer
nicht ohne Grund beginnt eine Subroutine zu schreiben und möchte ihm mit einem
freundlichen Kompilererror daran erinnern, daß im Falle:

sub hypotenuse; # oder
sub hypotenuse { }

etwas fehlt. Der geschickte Softwarearchitekt kann aber mit dem Operator namens
"yadayadayada" (zu deutsch bla bla bla) dem Interpreter mitteilen, daß er später
die Routine schreiben wird. Je nachdem ob er möchte das die jetzige Routine ein
fail, eine Warnung (C<warn>), oder einen Error (C<die>) liefert, kann er die
Schreibweisen C<...>, C<???> oder C<!!!> verwenden. Doch kehren wir zurück zum
ersten Beispiel.

Optionale Parameter

Manchmal braucht es aber Routinen, die je Situation unterschiedlich viele Parameter
bekommen. Wie deklarier ich das in der Signatur, ohne daß sich der Interpreter
beschwert? In dem ich den Paramtern ein Fragezeichen anhängt und sie damit als
optional markiert.

sub hypotenuse (Num+ $a, Num+ $b, Str $txt? --> Num+) {
my $h = sqrt( $a*$a + $b*$b );
say "$txt $h." if $txt;
return $h;
}

Man muß nur darauf achten, in der Signatur die optionalen Parameter nach den
Notwendigen zu positionieren, da alle Parameter in der Reihenfolge befüllt werden,
in der sie der Routine übergeben werden. Deshalb sollte auch die Reihenfolge der
optionalen Parameter sorgfältig überdacht werden, um sie von links nach recht von
mehr zu weniger wichtig zu sortieren. Der Befehl C<if $txt> wird auf keinen Fall
Probleme bereiten. Denn auch wenn kein Antwortsatz gewünscht ist und C<$txt> leer
bleibt, wird die Variable auf jeden Fall mit C<undef> initialisiert. Um andere
default-Werte festzulegen könnte man schreiben:

Num+ sub hypotenuse (Num+ $a, Num+ $b, Str $txt?) {
my $h = sqrt( $a*$a + $b*$b );
$txt //= 'Länge:
say "$txt $h.";
return $h;
}

oder

Num+ sub hypotenuse (Num+ $a, Num+ $b, Str $txt = 'Länge: ') {
my $h = sqrt( $a*$a + $b*$b );
say "$txt $h.";
return $h;
}

Da optionale Parameter auch an der Zuweisung erkannt werden, darf das Fragezeichen
im letzten Beispiel weggelassen werden. Das Gegenteil des Fragezeichens ist das
Ausrufezeichen (siehe dem Bedingunsoperator C<?? !!>) Deshalb werden notwendige
Parameter mit einem angehängten C<$var!> deklariert, was aber bei positionalen
Parametern nicht notwendig, da default ist.

Schlürfende Parameter

Manchmal ist es aber auch praktisch eine unbekannte Anzahl von Parametern in einem
Array zusammenzufassen. Dies erreicht man durch einen Stern als Präfix:

sub summe (*@a) { + @a }

Aber auch Hashes und Skalare aller Art dürfen als "slurpy" deklariert werden. Im
folgenden Beispiel implementieren wir Perl's map-Funktionen. Mit dem Unterschied,
daß bei unserem C<map> der anonyme Block an beliebiger Stelle stehen darf und der
der Interpreter anhand der Signatur die Parameter passend zuordnet.

sub map (&code, @werte) {
return gather for @werte -> $wert {
take $code($wert);
}
}

Würde die Sigantur C<(@werte, &code)> lauten, müsste der Block immer an letzter
Stelle übergeben werden.

Benannte Parameter

Wie angedeutet waren alle bisherigen Parameter positional, richten sich also nach
der Reihenfolge im Funktionsaufruf.

sub hypotenuse ($a, $b) {
sqrt( $a*$a + $b*$b );
}

Selbst bei einem Aufruf wie C<hypotenuse($b, $a)> würde der Inhalt der Variable
C<$b> in den Parameter C<$a> kopiert werden und analog Variable C<$a> in C<$b>.
Es gibt jedoch sehr gute Gründe Parameter manchmal direkt beim Namen anzusprechen.
Der Quellcode wird nachvollziehbarer und nicht jeder kann sich die Reihenfolge
von 12 Parametern für jede Routine oder Methode merken. Oft ist auch nicht die
Eingabe von jedem Parameter notwendig, aber wie sag ichs dem Computer, daß ich
diemal gerne die Parameter 3, 5 und 12 übergebe. Selbst die bereits vorgestellten
optionalen Parameter waren positional. Aus der Überlegung erschließt sich auch
warum in Perl 6 benannte Parameter per default optional sind. Wie bekannt kann
das angehängte C<!> sie erzwingen. Benannte Parameter erkennt man am Präfix C<:>.
Und sehr zu beachten: sie folgen den positionalen Parametern in der Signatur.

sub new(:$parent, :$ID, :$value :@size, :@pos, :@item, $:style)

Es gab Moment, da wünschte ich mir beim WxPerl-Programmieren Perl 6 wäre schon da
und z.B. die C<new>-Methode einer Combobox hätte eine solche Signatur. Weil beim
erzeugen des Widget sind bei mir nur die Eltern (Platz in der Objekthierarchie)
und der Sytle (Aussehen und Verhalten) wichtig. Die ID lass ich autogenerieren,
Größe und Position bestimmen die Sizer und Textwert und die Item werden bei Bedarf
gesetzt.

my $cb = Wx::Combobox.new( parent => $win, :style($style) );

So würde mir das gefallen. Zu Demonstrationszwecken enthält das letzte Beispiel
beide Paar-Schreibweisen. Sie wurden bereits in der vorigen Folge erläutert. Nur
ließe sich hier C<:style($style)> auch zu C<:$style> zusammenfassen.

Hat eine Routine keine Signatur, erhält sie ihre mt Namen zugewiesenen Parameter
aus C<%>, so wie C<@> nur die positionalen Parameter enthält. Verwendet man
Platzhalter-Variablen wie C<$^a> in Routinen ohne Signatur (obwohl die nur für
einfache Blöcke gedacht sind), erscheinen die Werte dieser Variablen nicht mehr
in C<%> oder C<@>.

Möchte man ein Paar als positionalen Parameter angeben, muß er in runde Klammern
gesetzt werden. Wie nachfolgend zu sehen, kann man die umschließenden Klammern
einer Signatur meist weggelassen werden.

# Aufruf mit einem positionalem Argument
Wx::Combobox.new (:parent<$win>), ;

Ich weiß auch: Tk und viele andere Module kennen heute bereits benannte Parameter.
Die Übergabe eines anonymen Hashes hat zumindest optische Ähnlichkeiten, Typen
und Anzahl der Geforderten Parameter werden dabei nicht geprüft. In Perl 6 könnte
man aber auch eine Routine mit einem Hash aufrufen. Damit Perl die Paare des Hashs
als Name und Wert benannter Parameter wertet muss der mit einem senkrechten Strich
dereferenziert werden.

Wx::Combobox.new( |%default );

"|" ist keine Sigil für einen Datentyp, so wie ein "&" für Codereferenzen steht,
es dient lediglich zum interpoliert in den Capture-Kontext, vergleichbar mit "@@",
daß für den bereits behandelten slice- oder auch multislice-Kontext steht.

Was zum $@%& sind Capture?

Von allen Neuheiten in Perl 6 fordern I<Capture> wohl am stärksten die Fähigkeit
sich neue Nervenverbindungen wachsen zu lassen, denn soweit mir bekannt, gibt es
nichts Vergleichbares in anderen Sprachen. Ein Capture ist ein Datentyp, der einem
Skalar zugewiesen wird und alle Parameter eines Routinenaufrufs speichern kann.
Da Signaturen sowohl positionale als auch benannte Parameter haben können,
erscheinen Capture anfangs als seltsame Hybride aus Array und Hash. Nur anders
als die Letztgenannten kann eine Capture nicht nachträglich verändert werden.
Sie ist "immutable" wie eine Liste. Weil es jetzt keine Referenzen mehr gibt
bekamen Capture den "\" vererbt, der von nun an "capture composer" heißt.

my (@a, $b, %c) = 1 .. 5, 6, {'sonnen' => 'schein'};
$capture = \(@a, $b, %c);

Diese Capture enthält keine Referenzen auf die Variablen sondern nur die Inhalte.

Anstatt zu referenzieren kann man in Perl 6 einen Alias auf eine Variable in der
Symboltabelle erstellen. Dies geht mit einem sehr einfachen Syntax und gänzlich
ohne Typeglobs.

$alias := $kathete;
$kathete = 5;
say $alias; # ist 5
# bindet während Kompilierung
$alias ::= $kathete;

Multi Sub's

Erinnern wir uns des ersten Beispiels. Wollte man eine Routine schreiben die die
Länge einer beliebigen Seite des rechtwinkligen Dreiecks berechnet, würden die
bisher vorgestellten Mittel nicht ausreichen. Mit benannten Parametern wäre die
richtige Zuordnung der Seiten gesichert, aber nicht die Forderung, daß 2 von 3
gegeben sein müssen. Eine Lösung bestünde darin das Problem aufzuteilen, was Perl
völlig neue Möglichkeiten eröffnet:

multi sub pythagoras (:$kathete!, :$kathete!) {
sqrt(@kathete[0]**2 + @kathete[1]**2);
}

multi sub pythagoras (:$kathete!, :$hypotenuse!) {
sqrt($hypotenuse**2 - $kathete**2);
}

Das Schlüsselwort C<multi> kündigt an, daß es mehrere Routinen gleichen Namens
gibt. Mit einem C<only> könnte ausschließen, das nachträglich noch eine C<multi>
zu einem Namen deklariert wird. Das ist aber meist nicht notwendig, da normale
C<sub> per default "C<only>" sind.

Wird die Routine mit C<pythagoras( :kathete<3>, :hypotenuse<5> );> aufgerufen,
prüft der Interpreter welche Signatur zu den Parametern passt. Manch einer ahnt
es schon. Auch dafür wird intern wie bei C<gigen/when> der "smartmatch" benutzt.
Es kann auch sehr praktisch sein selbst zu überprüfen ob ein Satz von Parametern
bei einer Routine Erfolg gehabt hätte.

$capture ~~ &routine.signature;

Parameter Traits

Alle bisherigen Paramter konnten in der Routine nicht verändert werden, was meist
sinnvoll ist, aber zuweilen unpraktisch. In diesen Fällen können einzelne Parameter
als veränderbar (rw steht für read/write) gekennzeichnet werden, was einer "<->"
Zuweisung in I<Pointy-Blocks> entspricht.

sub incr (*@vars is rw) { $_++ for @vars }

Der Befehl C<is> definiert I<Traits> (Charakteristiken) von Variablen. Das sind
neben dem Inhalt zusätzliche Werte oder Eigenschaften die zur Kompilierungszeit
Variablen gegeben werden können. Im Gegensatz dazu werden mit C<but> I<Properties>
(zusätzliche Laufzeiteigenschaften) bestimmt. Somit wird der alte Perl 5-Witz
"0 but True" lauffähiger Code.

Eine andere Möglichkeit veränderbare Parameter zu erhalten ist der Trait C<copy>.
Wie der Name aussagt sind solche Parameter veränderbare Kopien der übermittelten
Variablen.

Eingewickelte Routinen

Es gibt sogar Situationen da muß man eine Signatur rückwirkend anpassen. Unsere
großartige C<hypotenuse>-C<sub> könnte Teil eine Matematik-Bibliothek sein die
wir benutzen wollen. Sie kann sogar die Hypotenuse berechnen, wenn ein Winkel und
die gegenüberliegende Seitenlänge gegeben ist. Nur leider rechnet sie mit I<gon>
(Neugrad) und unser Programm mit I<Grad> (Altgrad). Die Bibliothek zu verändern
kommt nicht in Frage, da die Patches in jede neue fehlerreduzierte Version der
Bibliothek eingepflegt werden müssten. Zum Glück gibt es auch dafür in Perl 6
eine elegante Lösung.

sub hypotenuse($l, $winkel) {...}
$handle = &hypotenuse.wrap( { callwith( $^l, $^winkel/360*400 ) } );
# funktioniert einwandfrei
hypotenuse(2,20);
&hypotenuse.unwrap($handle);

Der letzte Befehl hebt die Umhüllung auf und es können selbstverständlich beliebig
viele Umhüllungen stattfinden. Sind irgendwelche andere Vor- und Nachbereitende
Tätigkeiten auszuführen und die Parameter sollen unverändert an die ursprüngliche
Routine weitergereicht werden, kann man statt C<callwith> auch C<callsame> nehmen.
Beide Befehle liefern die Ergebnisse der originalen Routine, die dann noch nach
Wunsch nachbereitet werden können.

Rückgabekontext

Nachbearbeitungen werden aber oft vermieden, wenn die Routine auf den Kontext
eingeht in dem sie gerufen wird. Das ist meist eine Signatur über alle Variablen,
denen das Ergebnis der Routine zugewiesen wird. Diese Signatur erhält man mit dem
Befel C<caller.want> und man könnte ohne Damian Conways Modul I<Contextual::Return>
schreiben:

given caller.want {
when :($) {...} # Skalarkontext
when :(*@) {...} # Arraykontext
when :($ is rw) {...} # Ein lvalue wird erwartet
when :($,$) {...} # 2 Werte werden erwartet
...
}

Für all das gibt es auch noch eine andere Schreibweise.

if want.item {...}
elsif want.list {...}
elsif want.void {...}
elsif want.rw {...}

Dieser Kontexte gibt es noch vieler mehr. Auch ist C<.want> bei weitem nicht die
einzigste Methode zur Introspektion, aber ich will hier kein Handbuch schreiben,
sondern nur einige Möglichkeiten andeuten. Eine vollständige Auflistung aller
Details soll das Tutorial in der Wiki unter
http://wiki.perl-community.de/bin/view/Wissensbasis/PerlTafel werden. In diesem
Beispiel wäre ein C<want> anstatt C<caller.want> ausreichend gewesen, aber würde
der C<return>-Befehl innerhalb eines Blocks stehen, wären C<caller.want> und
C<context.want> verschieden.

C<return> verlässt immer die innerste umgebende Routine. Wird lediglich gewünscht
den Block zu verlassen, empfiehlt sich C<leave> zu nehmen. Logischerweise wird nur
der Rückgabewert von C<return> gegen die Signatur der innersten Routine "gematcht".

Module und Scope

Was wäre Perl ohne Module. Deshalb bietet Perl 6 auch für Modulauthoren etliche
Verbesserungen zur Vorgängerversion. Da aber in diesem Bereich vieles noch nicht
in trockenen Tüchern ist, jetzt nur einige Grundzüge. Mit C<package> werden
weiterhin Namensräume definiert, für Namensräume mit zusätzlichen Eigenschaften
gibt es jetzt C<module>. Der Befehl C<module Name;> besagt, daß für den Rest der
Datei der Namensraum I<Name> gilt. Für mehrere Module in einer Datei schreibe man
C<module Name{ ... }>. So können Namensräume auch verschachtelt werden und mit
C<my module Name { ... }> Module sogar als lexikalisch lokal bestimmt werden.
Analog dazu dürfen auch Subroutinen jetzt mit C<my> lokal sein. Standartmäßig
entspricht aber weiterhin ein C<sub routine {...}> einem C<our sub routine {...}>.

Eine der Hauptfähigkeiten von Modulen ist das Exportieren von Routinen. Dazu
benötigt man kein C<use Exporter;> mehr, sondern markiert die entsprechenden
Routinen mit einer I<Trait> als C<is export>. C<sub>-Traits haben ihre Position
nach der Signatur. Module werden wie bekannt mit C<use> oder C<require> geladen,
jedoch wurden auch diese Befehle wesentlich mächtiger um einige Probleme zu lösen
die ein wachsendes CPAN mit sich bringt.

use Dog:<1.2.1>;

So fordert man z.B. eine spezielle Version an. Noch genauer wäre:

# bitte keine Version 1.2.7
use Dog:ver(1.2.1..^1.2.7);

Auch ein optionaler Mechanismus zur Authentifizieren von Autoren ist im Syntax
vorgesehen, jedoch noch nicht voll ausgereift.

Namensräume mit weitaus mehr Eigenschaften werden mit C<class> erzeugt. Die OOP
wird aber Stoff der nächsten Folge sein.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial Part 8


Herzlich willkommen auch zum achten und letzten Teil dieses tiefsch�rfenden
Tutoriums f�r werdende Perl 6-Programmierer. Diesmal wird es sogar noch eine Ebene
tiefer gehen, zu den Interna der Sprache und wie sie befragt und ver�ndert werden.
Da diese Bereiche st�rker theorielastig sind und von Pugs und Rakudo bisher kaum
implementiert werden, besteht diese Folge aus mehr Text und weniger Beispielen
als �blich.

Wozu Metaprogrammierung?

Von Anfang an ging es bei diesem Projekt nicht nur darum vergangene Irrt�mer
auszub�geln und den aktuellen Stand dynamischer Sprachen in vielem wieder ein- und
�berholen. Perl 6 sollte auch nach l�ngerer Reifung ebenfalls lange halten. Was
bedeutet, da� die Sprache f�hig sein mu�, k�nftige Anpassungen zu unterst�tzen,
ohne das die Implementation des Interpreters angefasst werden wird. Einfacher
gesagt: Perl 6 sollte bessere M�glichkeiten der Metaprogrammierung erhalten,
als die derzeit ber�chtigten I<Sourcefilter>, die abgeschafft wurden, da sie viel
zu langsam und fehleranf�llig sind, um in echtem Produktionscode eingesetzt zu werden.

Das ganze hat aber noch eine anderen Grund. Viele der weitverbreiteten Sprachen
haben etliche Varianten oder auch Ableger, da verschiedene Fachbereiche oder
Anwendergruppen verschiedene Betrachtungsweisen kennen oder bevorzugen.
Um die Aufsplitterung von Perl in Derivate wie es z.B. mit PHP geschah zu verhindern,
mu� Perl noch wandlungsf�higer werden.
Und nur eine gemeinsame Basis macht eine Infrastruktur und damit alle Beteiligten
wirklich m�chtig (siehe CPAN).
Gerade in den letzten Jahren wird das mit dem Modewort I<domain specific language>
(DSL) zusammengefasst, wenn Sprachen einer Aufgabenstellung angepasst werden k�nnen,
um diese effektiv und f�r die mit der Materie Vertrauten verst�ndlich zu l�sen.
Flexibilit�t (TIMTOWTDI) hatte sich Perl schon immer auf die Fahnen geschrieben
und dies wird mit Perl 6 nur noch etwas weiter getrieben. Perl 6 ist in Wirklichkeit
eine Metaprogrammiersprache, die sich zur Laufzeit in fast alles Denkbare verwandeln
kann. Dies ist in Ordnung so, mein Larry Wall, da alles erlaubt sein soll,
wenn man es vorher explizit deklariert. Und Lernende werden mit einem ausgewogenem
Satz vordefinierter Befehle zu einem "guten Stil" angeleitet, bis sie f�hig werden,
alle Regeln zu ver�ndern. Und je mehr sie lernen diese Regeln zu ihrem Vorteil
zu �ndern, umso k�rzer, effektiver und oft auch lesbarer werden ihre Programme.

Dieser Ansatz bringt nicht nur Freiheit f�r den Programmierer sondern erlaubt die
immer komplexer werdenden Softwaresysteme kompakter und beherrschbarer zu schreiben.
Ein Blick in die Java-Welt zeigt die Nachteile hierarchisch-logischer Strukturen,
die oft zu unbeweglich und �berladen und damit schwer lesbar und ver�nderbar sind.
Aber selbst in der Welt von Perl und Ruby lernte man in den letzten Jahren, da�
die zuerst gepriesenen Web-Frameworks schnell sehr umfangreich werden. Jedes ist
eine Welt f�r sich, die erlernt sein will, obwohl sich viel Funktionalit�t �hnelt.
Deshalb entstanden in letzer Zeit mit Rack, Mojo und anderen Unterfangen Software,
die sich auf einzelne Probleme konzentriert, um diese in logischen Einheiten mit
eine m�glichst einfachen API handhabbar zu halten. Die Idee dahinter ist nicht neu,
denn Perl 1.0 brachte Shellbefehle in die C-Syntax brachte, um komplexere Probleme
kurz und und dem menschlichen Denken nah zu l�sen. Sp�ter erf�llten in Perl 5.n
gute Module den gleichen Zweck, nur das sie bereits von Programmierern der Sprache
zugef�gt werden konnten, ohne den C-Quellcode von Perl anzufassen. Das neue Perl 6
ist eine logische Fortsetzung dieser Entwicklung zu einer Sprache die quasi "fl�ssig" ist,
und in verschiedene Situationen die Syntax annehmen kann, der dem Ideal des Benutzers
entspricht. Dadurch fallen Barrieren, die bisher durch Programmiersprachgrenzen
aufgestellt wurden. Gro�e Systeme bestehen zunehmend aus Teilen die in verschiedenen
Sprachen und Versionen geschrieben wurden, die sich nur in Details unterscheiden
k�nnen, aber dennoch von zus�tzlicher I<Middleware> "zusammengehalten" werden m��en.
Diese Teilsystem werden oft kaum mehr angefasst, da sie ausgereift, zuverl��ig
aber nicht immer leicht wartbar und auch nicht leicht erneuerbar sind. Deshalb
wachsen diese Flickenteppiche zu ungeahnten Komplexit�ten und es gibt bereits
Lehrst�hle f�r Professoren, die Menschen beibringen wollen mit solchen Unget�men
umzugehen. Besser w�re es doch, wenn solche Software gleich in Perl 6, vielleicht
auch Ruby oder Lisp geplant wird, um diese Komplexit�t nicht entstehen zu lassen.
Und selbst wenn "unber�hrbare" Software Teil einer Anwendung ist, kann eine sehr
anpassungsf�hige Sprache Kommunikation mit wenig Programmieraufwand erm�glichen,
und Perl bleibt was es immer war: eine I<glue-language>.

Wo beginnt Metaprogrammierung ?

Wenn eine kleine I<sub> den verf�gbaren Wortschatz erweitert, ist das bereits
Metaprogrammierung? Das kommt auf den Fall an. Subroutinen k�nnen dazu verwandt
werden etwas Ordnung in Spaghetticode zu bringen, was unter das Schlagwort
"strukturierte Programmierung" f�llt. Da die Errungenschaften von C<if>, C<while>
und Routinen seit den 70er-Jahren Allgemeingut wurden, f�llt der Begriff heute selten.
Wesentlich h�ufiger h�rt man dieser Tage dagegen das Wort "funktionale Programmierung".
So hei� ein Programmierstil der sehr abstrakt ist und daher in die Metaprogrammierung
hineinragt. Mark Jason Dominus beschreibt in "Higher Order Perl" genauer wie das
in Perl aussieht (Buchrezension in $foo Winter/2008). Das Buch ist wohl zum Teil
nach den "Higher Order Functions" benannt. Das sind Routinen die eine Aufgabe
sehr abstrakt l�sen und deshalb sehr vielseitig einsetzbar sind. Viele Perlianer
verwenden bestimmt solche "Higher Order Functions", ohne den Begriff zu kennen.
Z.B. C<map> und C<grep> fallen in diese Kategorie. Aber mit Perl 5.0 konnte man
auch bereits selber solche Functionen schreiben, da schachtelbare Namensr�ume f�r
Variablen und Codereferenzen als Parameter anwendbar waren. �hnlich der OOP war
in Perl 5 fast alles m�glich, nur vieles jetzt einfacher. Eine wichtige Technik
in der funktionalen Programmierung ist z.B. das I<Currying>. Auf deutsch: das
Erstellen einer Codereferenz auf eine Funtion h�herer Ordnung (einen Alias), bei
der bestimmte Parameter festgelegte Werte haben und nur die restlichen Parameter
bestimmt werden k�nnen. Also wenn eine Funktion C<potenz> beliebige Potenzen
berechnet(ja es ginge auch mit C<**>, aber folgender Code entspricht einem
funktionalem L�sungsansatz):

subset Num+ of Num where { $_ > 0 };
subset Num- of Num where { $_ < 0 };
subset Zero of Num where { $_ == 0 };

multi sub potenz (Num :$basis!, Zero :$exponent!) {
return 1;
}

multi sub potenz (Num :$basis!, Num- :$exponent!) {
return 1 / potenz( $basis, -$exponent);
}

multi sub potenz (Num :$basis!, Num+ :$exponent!) {
return $exponent * potenz( $basis, $exponent - 1 );
}

Ein Funktion C<quadrier> w�rde ich nun explizit wie folgt erhalten:

&quadrier := &potenz.assuming( exponent => 2 );
# alternative Schreibweise:
&quadrier := &potenz.assuming( :exponent(2) );

Die l��t sich wie erwartet aufrufen:

say quadrier(5); # ergibt 25

C<.assuming> (englisch f�r angenommen) dr�ckt in einer Alltagssprache genau aus
worum es hier geht: C<quadrier> entspricht C<potenz>, unter der Annahme, da� der
Exponent 2 ist. Das C<:=> ist keine Zuweisung sondern ein I<binding>. P6 kennt
weder direkten Manipulation der Symboltabelle mit Typeglobs noch Referenzen (der
Schr�gstrich (I<backslash>) erzeugt jetzt I<captures>, siehe Teil 6). Um einen
Alias auf den Inhalt einer Variable zu erhalten, bindet man diese Variable mit
C<:=> an eine Andere.

my $planeten = 7;
my $planety := $planeten;
$planeten = 9;
say $planety; # gibt 9
say "yes" if $planety =:= $planeten;

Das letzte Beispiel f�hrt die Ausgabe aus, da die Variablen an das gleiche Objekt
gebunden sind. Ein Binden zur Kompilierungszeit mit der Schreibweise C<::=> mag
selten gebraucht werden, aber das Ausf�hren von Routinen zum fr�hstm�glichen
Zeitpunkt wesentlich �fter. In Perl 5 schrieb man dazu den Code in einen BEGIN-Block,
was weiterhin m�glich ist, aber Perl 6 kennt noch ein anderes Konzept, das dem
�hnelt, aber weitaus m�chtiger ist. Manche behaupten sogar, da� vor allem diese
Macros LISP m�chtiger machen als alle anderen Sprachen, was in den 60er und 70er
Jahren auch gestimmt haben mag.

Die wunderbare Welt der Macros

Macros sind (wie angedeutet) Routinen, die beim Kompilieren ausgef�hrt werden.
Im Gegensatz zu einer C<sub>, �ndern sie die Sprache, da sie statt eines Wertes,
einen AST (Baumstruktur, die als Zwischenform beim Kompilieren entsteht) liefern,
der beim Kompilieren anstelle jedes Macroaufrufes in den AST des Programmes
eingef�gt wird, bevor die Ausf�hrung beginnt.

In C ist das bei weitem einfacher. Hier sind Macros lediglich Textbausteine die
an die mit dem Makronamen markierten Stellen im Quellcode eingef�gt werden, bevor
der Kompiler sein Werk beginnt. Die alten Perl 5-Sourcefilter taten ihr Unwesen
nach dem gleichen Prinzip, nur da� hier die volle Kraft der P5-Regex am Werke war.
Das kann subtile, schwer zu entdeckende Fehler erzeugen, da jeder Macroprozessor
blind f�r die Bedeutung der Sprachsyntax ist und der von ihm erstellte Quelltext
nirgends einsehbar ist. Perl 6-Macros haben standardm��ig (wie alle Bl�cke) keine
Seiteneffekte auf die lexikalische Struktur der umgebenden Quellen.
So etwas nennt die Fachwelt: hygienische Macros. Folgende Makros sind hygienisch:

macro summe { 3 + 4 }
macro summe { '3 + 4' }
macro summe is parsed { 3 + 4 }

C<parsed> ist ein Trait von Routinen da� nur Macros wirklich ben�tigen, aber da
es default ist, kann es auch weggelassen werden. Und auch die Verwendung der
Macros ist denkbar einfach.

say 2 * summe; # 14
say 2 * summe(); # 14
say 2 * &summe(); # 14

Auch wenn alle Aufrufe C<14> ergeben, so sind sie nicht identisch, da der dritte
erst zur Laufzeit aufgel�st wird. Doch manchmal sind dreckige Macros genau was
man m�chte und dann schreibt man.

macro summe is reparsed { 3 + 4 }

Wird dieses Macro im vorigen Beispiel aufgel�st, hie�e das Ergebnis C<10>.
Denn dieses Macro gibt nur einen String zur�ck, der zusammen mit dem umgebenden
Quellcode compiliert wird. Dabei gilt die alte Regel: Punkt- vor Strichrechnung.
Da diese Funktionsweise derartige Probleme provoziert, hat sie die beschriebene
Kindersicherung und es wird standardm��ig von Macros ein AST zur�ckgegeben.
Aber auch au�erhalb von Macros kann dies getan werden. I<quoting> mit dem Adverb
C<:code> (siehe letzte Folge) und ein C<quasi> vor geschweiften Klammern oder
Anf�hrungszeichen kann das (I<quasiquoting>) bewirken.

return quasi { say "foo" };
return Q :code / say "foo" /;

Beide Zeilen liefern keine Referenz auf eine Routine sondern ein kompiliertes
St�ck Programm. Folglich ist ein Macro eine zu C<BEGIN> ausgef�hrte Routine
deren R�ckgabewert derart C<quasi> kommentiert (I<gequoted>) ist. Dies ist eine
sehr elegante Eigenschaft von Perl 6, da� jedes Sprachelement mit den Bordmitteln
der Sprache beschrieben werden kann. Deswegen kann die Syntax, so reichhaltig sie
auch scheinen mag, auf einen wesentlich kleineren Kern reduziert werden, aus dem
sich beliebige Sprachen aufbauen lassen. Deshalb gilt f�r Perl 6 was John Foderaro
einst �ber Lisp sagte: "es ist eine programmierbare Programmiersprache".
Weil Perl syntaktisch viel reichhaltiger als Lisp ist, da� nur Funktionsnamen,
Wertelisten und runde Klammern kennt, m��en Perl-Macros wesentlich mehr k�nnen,
um wirklich alle Sprachbestandteile erweitern zu k�nnen. Einen neuer Operator
(z.B. f�r die mathematische Fakult�t-Funktion) wird so erzeugt:

macro postfix:<!> { * 1..$^n }
macro postfix:('!') { * 1..$^n }

Das gew�hlte Operatorsymbol kann auch in andere Klammern als den Spitzen geh�llt
sein, aber der Vorsatz "postfix:" ist entscheidend, da wir einen Postfix-Operator,
also einen nachgestellten Operator (wie in C<$p++>) definieren wollen. Um noch zu
bestimmen wo der neue Operator seinen Platz in der Vorrangtabelle hat, k�nnte man
hinzuf�gen:

macro postfix:<!> is equiv(&postfix:<++>) { ... }

Das k�nnte man auch mit C<is tighter> oder C<is looser> definieren. Dann bek�me
der Operator eine eigene Spalte in der Vorrangtabelle die jeweils �ber oder unter
dem angegebenen Operator liegt.

Diesem Schema entsprechend gibt es eine lange Reihe von Schl�sselw�rter die jede
Art von Operator oder Schl�sselwort bezeichnen. Es lassen sich eigne Arten des
I<Quoting>, Regex-Befehle, Spezialvariablen oder neue sekund�re Sigils einf�hren.
Wenn jemand XML-Kommentare in seinem Perl haben m�chte so reicht ein:

macro circumfix:�<!-- -->� ($text) is parsed / .*? / { "" }

C<$text> ist der Parameter, der den Text zwischen den Kommentarzeichen beinhaltet.
Nach dem C<is parsed> steht die Regex mit der geparsed werden soll. In diesem
Fall hat das <reparsed> den Vorteil, da� die Regex mit Regeln formuliert werden
kann die erst sp�ter definiert werden.

Da entlang Alice

Aber die Manipulation der Sprache kennt noch eine Ebene. Es ist sogar m�glich
die Regeln zu �ndern mit denen der Interpreter den Quellcode einliest. Seine
Arbeitsweise wird in der STD.pm mit der Hilfe von P6-Regex-Grammatiken definiert.
Wie diese formuliert werden und aufgebaut sind, behandelte die letzte Folge.

Intern wird die Sprache in mehrere Teilsprachen ("slangs") gegliedert.
Die Kernsprache ist z.B. eine Grammatikobjekt, auf das mit C<$~MAIN> zugegriffen
werden kann. Die Regeln f�r das Kommentieren stehen unter C<$~Q>.
Und die Regex die bestimmen wie Regex einzulesen sind, finden sich unter C<$~Regex>.
C<~> ist die Twigil dieser Sondervariablen. Und da Grammatiken nach au�en normale
Objekte sind, k�nnen sie abgeleitet und ver�ndert werden wie jedes andere Objekt
auch. S�mtliche �nderungen gelten nur f�r den aktuellen Namensraum (I<scope>)
und die I<defaults> k�nnen jederzeit wiederhergestellt werden, da sie unter
C<%?LANG> gespeichert sind.

Zeig mir dein Inneres!

Objekte k�nnen in Perl 6 sehr stark durchleuchtet und ver�ndert werden. Jede
Elternklasse, jede Methode und jede Signatur (mit C<$obj.methode.signature>) kann
abgefragt oder verschiedenst gepr�ft werden. C<$obj.WHERE> nennt die Speicheradresse,
C<.WHAT> den Typ, was in etwa dem Befehl C<ref> in Perl 5 entspricht.

Es lassen sich mit Roles beliebige Methoden zu Laufzeit in ein Objekt einf�gen,
aber der direkteste Weg daf�r ist wohl:

augment slang Regex {
token regex_metachar:<^> { ... }
}

C<augment> (englisch f�r einblenden) f�gt nur Regeln (Methoden) in die Grammatik
(Klasse) ein, soll eine gesamte Slangdefinition ausgetauscht werden, dann ist
C<supersede> (englisch f�r �berlagern) das Mittel der Wahl.

Es gibt noch viele weitere Sondervariablen, wie f�r den umgebenden Block
(C<&?BLOCK>) oder die umgebende Routine (C<&?ROUTINE>) mit denen sich weit mehr
tun l��t als noch in Perl 5, z.B. lassen sich alle Sprungmarken im aktuellen Block
mit C<&?BLOCK.labels> auflisten. Auch C<&?ROUTINE.name> ist praktisch, wenn man
nicht merh wei� wo sich die Ausf�hrung gerade befindet. Doch dies sieht man alles
in den Tabellen im Anhang B meines Wiki-Kompendiums, siehe Artikelende.

Was sich sonst noch tat

Zu Beginn dieses Tutorials k�ndigte ich an, mit jedem Teil auch alle �nderungen
bereits erw�hnter Syntax zu dokumentieren. Doch zum Gl�ck waren es weit weniger,
als angenommen, soda� eine kleine Aktualisierung am Ende des letzten Teils gen�gt.
Gleich im ersten Teil wies ich auf eine potentielle Stolperfalle:

$b = =$a; # Zuweisung einer Zeile aus einem Datenstrom
$b == $a; # numerischer Vergleich

Diese ist mittlerweile behoben, denn der prefixe Operator C<=> wurde ersatzlos
gestrichen. Statt dessen sollte man die Methoden C<.lines> und C<.get> verwenden.
C<.lines> liefert einen Iterator und C<.get> tats�chlich Textzeilen.

my $name = "artikel.txt";
my $handle = $name.open err die "Kann '$name' nicht �ffnen: $!";
my $ganzer_inhalt = $handle.slurp;

for $handle.lines > $zeile { ... }
while $handle.get
> $zeile { ... }
$zeile = $handle.get;
$handle.close;

Da der Kopf einer C<while>-Schleife im Skalarkontext und C<eager> evaluiert wird,
w�rde C<.lines> dort einen Array mit allen Zeilen liefern. Da jedoch C<for> den
Arraykontext forciert der standartm��ig C<lazy> ist, wird dort bei jeder Iteration
jeweils eine Zeile �berwiesen. Im Skalarkontext w�rden alle Zeilen mit C<~>
verbunden geliefert.

Der neue Metaoperator C<R> kam hinzu. Er vertauscht lediglich die Operanden,
R steht f�r I<reverse> (englisch r�ckw�rts).

say 3 R- 4; # sagt 1
say 2 R** 3; # sagt 9

Der Kreuz-Metaoperator wurde vereinfacht. Von nun an reicht es dem Operator ein
C<X> voranzustellen, vorher hie� das noch z.B. C<X~X>.

<a b> X~ <1 2> # <a1 a2 b1 b2>

Der Hyper-Metaoperator l��t sich aber nicht so vereinfachen, da die Pfeile
(<< und >>) bestimmen, wie der Operator auf Dimensionalit�t der jeweiligen Seite
reagieren soll.

Neben autogenerierten positionalen Parametern (erkennbar an der twigil C<^>), gibt
es nun autogenerierte benannte Parameter, deren Twigil C<:> sich nahtlos in die
Syntax zur Deklarierung benannter Parameter einf�gt.

Selbstverst�ndlich gab es noch weit mehr �nderungen, doch diese sind oft subtiler
oder liegen au�erhalb der behandelten Themen. Einen tieferen Einblick gew�hrt das
Tutorial in der Wiki der deutschen Perl-Community unter
L<wiki.perl-community.de/cgi-bin/foswiki/view/Wissensbasis/PerlTafel>,
oder einfacher zu tippen: L<de.perl.org> und dann auf Wiki > Wissensbasis > Tutorials
klicken. Viele hilfreiche Texte beinhaltet auch die Seiten von Moritz Lentz unter
L<perl-6.de>. Dieses Tutorial wurde jetzt in eine Wiki online gestellt unter:
Lhttp://wiki.perl-community.de/cgi-bin/foswiki/view/Wissensbasis/Perl6Tutorial
und kann nun von jedem kommentiert und verbessert werden. Ich habe vor es weiter
zu verbessern und auch ins Englisch zu �bersetzen, wo es in der Wiki der TPF
unter L[perl tablets] zu finden sein wird.


Previous Chapter | Overview

permalink
Perl 6 Tutorial Part 4


Still operators left?

Nach "Operatoren f�r Skalare" und "Operatoren f�r Arrays" ist sicher "Operatoren
f�r Hashes" der n�chste logische Titel. Nur leider gibt es keine speziellen
Operatoren f�r Hashes in Perl 6. Au�er vielleicht den "dicken Pfeil" ("fat arrow").
�hnlich dem Komma, da� einen Array erzeugt, weist der "=>" den Interpreter an,
einen Hash zu bilden. Deswegen wird er auch manchmal "hash composer" genannt.

Perl 6:
$ziffern = (7); # Zuweisung eines Wertes
$ziffern = 7,; # Referenz auf einen Array
%ziffern = 1 => 'adin', 2 => 'dwa';
%ziffern = {1 => 'uno', 2 => 'due'}; # geht auch

Da runde Klammern nun nur noch gruppieren und geschweifte Klammern per default
einen anonymen Codeblock darstellen, braucht der Interpreter auch diesen Hinweis
um zu erkennen, da� der Programmierer gerne einen Hash h�tte. Weil geschweifte
Klammern dar�ber hinaus immernoch f�r Hashreferenzen stehen k�nnen, wird in der
letzten Beispielzeile eine solche erzeugt. Nur wird diese, anders als in Perl 5,
automatisch dereferenziert, wenn sie einem Hash zugewiesen wird. Dieses Prinzip
wurde in der letzten Folge schon anhand von Arrays demonstriert. In Perl 5 war der
"fat arrow" nur eine lustige Schreibweise f�r ein Komma. Es half dem Programmierer
zu erkennen da� hier ein Hash ensteht, aber er h�tte genauso gut schreiben k�nnen:

Perl 6:
%ziffern = (0, 'null', 1, 'eins');
%ziffern = {1, 'jeden', 2, 'dva'};

Dies funktioniert auch weiterhin wunderbar. Aber nicht weil nun Hashes immer noch
Arrays mit gerader L�nge sind, sondern weil es eine Regel gibt, wie der Array in
einen Hash(kontext) umgewandelt wird. Und ja, in der zweiten Zeile liefert ein
anonymer Block einen Array zur�ck. In diesem Beispiel st�rt das nicht.Aber es kann
zu sehr unerwarteten Ergebnissen f�hren, da syntaktisch gleiche Bl�cke in
Sonderf�llen andere R�ckgabewerte haben k�nnten. Um es eindeutig zu halten,
k�nnen die Operatoren C<%> und C<hash> den Hashkontext erzwingen und anzeigen.

Perl 6:
$hashref = hash{ 0, 'null', 1, 'eins' };
$hashref = %( 0, 'null', 1, 'eins' );
# oder klassisch:
$hashref = { '1' => 'a', '2' => 'b', 3 => 'c' };
# entspricht \('1' => 'a', '2' => 'b', 3 => 'c');
# oder auch %( 1..3 Z 'a'..'c' );

Ein Hash kann aber auch in einen anderen Kontext �berf�hrt werden. Im boolschen
Kontext meldet er, ob er �berhaupt Schl�ssel besitzt, im numerischen Kontext
die Anzahl der Schl�ssel und im Stringkontext gibt er seinen gesamten Inhalt als
String aus.

Perl 6:
my %buch = ( 1..3 Z 'a'..'c');
? %buch # Bool::True
+ %buch # 3, entspricht %buch.elems
~ %buch # "1\ta\n2\tb\n3\tc\n"

Weit praktischer ist aber meistens die Ausgabe der folgenden, selbsterkl�renden
Hashmethoden, welche einen Array zur�ckgeben.

Perl 6:
%buch.keys; # (1, 2, 3), entspricht keys %buch
%buch.values; # ('a', 'b', 'c'), auch values %buch
%buch.kv; # (1, 'a', 2, 'b', 3, 'c')

C<keys> und C<values> sind altbekannte Perl 5-Befehle, die es selbstverst�ndlich
auch weiterhin gibt. Aus ihren Initialien leitet sich der Name der Methode C<.kv>
ab, die abwechselnd zugeh�rige Schl�ssel und Werte in einem Array aufz�hlt. Etwas
anders funktioniert dagegen die Methode C<.pairs>. Sie liefert key und value in
einem Skalar, also einen Array von Wertepaaren. Das Gleiche erh�lt man auch, wenn
ein Hash einem Array zugewiesen wird.

Perl 6:
%buch.pairs; # (1 => 'a'), (2 => 'b'), (3 => 'c')
@buch = %buch; # dito

Werdet ein Paar

Key und value in einem Skalar? Das mag zuerst nach einem Array oder vielleicht
einer Junction klingen, ist aber einfach ein Wertepaar. Das Paar geh�rt zu den
v�llig neuen Datentypen, die Perl 6 einf�hrt. Eigentlich sind es auch Paare, die
mit dem "fat arrow" erzeugt werden. In den Synopsen wird "=>" desweg oft auch
"pair constructor" genannt.

Perl 6:
my $pair = 'Jakub' => 'Helena';
$pair.isa(Pair) # ergibt Bool::True
$pair.key # 'Jakub'
$pair.value # 'Helena'

Paare k�nnen aber auch noch anders generiert werden.

Perl 6:
my $pair = :Jakub('Helena'); # oder k�rzer:
my $pair = :Jakub<Helena>; # <> ist das neue qw()

Selbstverst�ndlich funktioniert auch diese Schreibweise f�r Hashes.

Perl 6:
%regent = :Atreides<Paul>, :Harkonnen<Wladimir>, :Ordo<Executrix>;
$hashref = {pair <Atreides Paul Harkonnen Wladimir Ordo Executrix> };

Allerdings dient das nicht der optischen Vielfalt, sondern zeigt, da� Paare mehr
Aufgaben haben, als Hashbausteine zu sein (obwohl sie dazu sehr geeignet sind).
Ungeordnete Listen von Wertepaaren mit eindeutigen Schl�sseln entsprechen sicher
eher der Idee eines Hashes als Listen mit gerader L�nge. Das n�chste Beispiel
ist ein bewu�t herbeigef�hrter Unfall, der die Schw�che von Perl 5 demonstriert.
Einzelne Werte die dem Hash entnommen wurden k�nnen jede Zugeh�rigkeit zwischen
den Schl�sseln und Werten zerst�ren.

Perl 5:
my %h = (1=>2, 3=>4);
%h = grep {$_ % 2} %h;
say %h; # {1 => 3 }

Zum Gl�ck wurde f�r Perl 6 auch diese Schmuddelecke aufger�umt.

Perl 6:
%h = grep {$_.value % 2} %h.pairs;

Diese Fassung macht nicht nur mehr Sinn, sondern ist auch noch lesbarer, da hier
explizit steht, was geschieht. Sie kann auch keine unsinnigen Ergebnisse liefern,
da korrekterweise immer Paare durchgereicht werden. Doch um zu beweisen da� die
neue Ordnung nicht einschr�nkt, hier das Perl 5-Beispiel im Perl 6-Syntax.

Perl 6:
%h = grep {$_ % 2} %h.kv;

Parameterpaare und $@%

Mit Paaren lassen sich wundersame Datenstrukturen wie Hybride aus Hash und Array
erzeugen oder Paarlisten, in denen Schl�ssel mehrfach vorkommen k�nnen. Aber die
wichtigste Verwendung von Paaren au�erhalb der Hashes sind benannte Parameter.
Diese wurden in Perl 5 oft mithilfe von Hashes simuliert. Jedoch bietet Perl 6
daf�r einen expliziten Mechanismus der viele interessante Techniken erm�glicht.
Subroutinen und Parameter�bergaben sind aber das Thema der n�chsten Folge. Doch
selbst "Kernfunktionen" der Sprache haben benannte Parameter, welche die zweite
Syntax der Paare benutzen. Die in Folge 2 vorgestellten Dateitestoperatoren
C<:r :w :e> sind nichts weiter als Attribute des Objektes vom Typ Filehandle.
Dabei dr�ckt die Formulierung C<:r> aus: "Ich h�tte gerne den Wert, der unter dem
Schl�ssel "r" gespeichert ist". �hnliches gilt auch beim �ffnen einer Datei.

Perl 6:
my $fh = open "datei.txt", :r;

Die Datei wird im Lesemodus ge�ffnet, indem der benannte Parameter "r" gesetzt
wird. In dem Beispiel f�llt auch auf, da� Filehandle kein eigener Variablentyp
sind und auch keinen eigenen Namensraum mehr besitzen. Auch Verzeichnishandler
und Formate werden nun in Skalaren gespeichert. Die Formate weichen dazu aus dem
Sprachkern in ein Modul. Auch Token wie C<__LINE__> oder C<__PACKAGE__>, die man
als Systemvariablen mit eigenem, sonderbaren Namensraum ansehen k�nnte, sind in
ihrer neuen Schreibweise C<$?LINE> und C<$?PACKAGE> eher als Variablen erkennbar.
Ihr seltsamer Namensraum nennt sich nun "compiler hint variables". Er umfasst
Informationen, die ab dem Zeitpunkt der Kompilierung feststehen und auch nicht
ge�ndert werden d�rfen. Eine Zuweisung wie C<$?FILE = 'was anderes';> w�rde einen
Compile-Error ausgeben. Dieser Sonderstatus wird mit der sekund�ren Sigil (Twigil)
"?" markiert. Achtung: In Perl 6 bezeichnet Token eine sehr einfache Form einer
Regex.

Die Streichung der 4 Sondernamensr�ume f�r Variablen (Handler, Formate und Token)
sind weitere Beispiele des Grundsatzes: "keine Ausnahmen" und der Forderung nach
einem deklarativen Syntax, der den Inhalt eines Ausdrucks deutlicher sichtbar macht.
Die verbliebenen Namensr�ume k�nnen eindeutig an der Sigil unterschieden werden.
Oder einfacher: $wunder, @wunder und %wunder sind drei verschiedene Variablen.

Datentyp Code

Der vierte Namensraum ohne Sigil, ist der der Subroutinen. Und wie bei Hashes und
Arrays, k�nnen Referenzen auf Routinen ebenfalls in Skalaren gespeichert werden.

Perl 6:
my $coderef = sub { say "Moin #perl" }

Die besitzen dann den Datentyp "Code". (Das fehlende Semikolon ist kein Fehler.
Perl 6 verlangt nur dann ein solches, wenn mehrere derartiger Deklarationen ohne
den Befehl C<sub> in eine Zeile schreibt. Als Eselsbr�cke: ein "}" am Zeilenende
impliziert ein Semikolon.) Das C<sub> k�nnte man also auch weglassen, was nichts
am Datentyp und an der Benutzung der Variable �ndert.

Perl 6:
my $coderef = { say "Moin #perl" }
# dito, ebenfalls noch m�glich
my $cr = \{ say "Moin #perl" }
$cr.isa(Code) # ergibt Bool::True
$coderef(); # f�hrt den Block aus

Trotzdem sind die Unterschiede zwischen einer "sub" und einem Block so zahlreich,
da� man damit viele Seiten f�llen kann, sie alle aufzuz�hlen. Deshalb wird diese
Folge auch eher Bl�cke behandeln und erst die n�chste die Sonderform "Subroutine".
Einfache Bl�cke besitzen n�mlich nur Grundf�higkeiten und Befehle wie C<while>,
C<if>, C<package> und C<sub> f�gen dem weitere Funktionalit�t hinzu, weswegen sie
im Perl 6-Jargon Blockmodifikatoren genannt werden. Da diese Modifikatoren "builtins"
und keine Routinen sind, kann man sich jetzt nicht nur die runden Klammern sparen.
Es wird empfohlen auf sie zu verzichten. Und wenn nicht anders m�glich, sollte man
zumindest darauf achten, zwischen Modifikator und den runden Klammern Leerzeichen
zu lassen, es sei eine C<sub> namens C<if> m�chte angesprochen sein.

Perl 6:
if $a > 6 { say "Zu alt f�r den Kindergarten" }
if($a > 6) { ... } # Error

Doch beginnen wir mit einem nackten Block.

Closure

Bl�cke sind in Perl 6 alles, was von geschweiften Klammern umschlossen ist, soweit
nicht darin ein C<=>> oder C<pair> steht, oder die voranstehenden Operatoren C<%>
oder C<hash> eine Hashreferenz erzwingen. Sie teilen Code in logische Einheiten.
Dadurch werden die Quellen �bersichtlicher und verst�ndlicher. Es erlaubt aber
auch Variablen einem Block zuzuordnen. Das hat den gro�en Vorteil, da� man damit
heimt�ckische �berschneidungen mit gleichnamigen Variablen anderer Programmteile
ausschlie�en kann. Au�erdem spart es Arbeitsspeicher, wenn die Lebensdauer einer
Variable auf den Block begrenzt wird, in dem ihr Inhalt gebraucht wird.

Der erste Versuch in die Richtung war der Befehl C<local>, der bereits vor Perl 5
eingef�hrt wurde. Doch der �berlagert eine eventuell exisierende, gleichnamige
Variable tempor�r, bis die Ausf�hrung des aktuellen Blocks beendet ist. Deswegen
wurde die Anweisung in C<temp> umbenannt. Eine echte Bindung zwischen Variable und
Block erzeugt C<my>, welches Perl 5 brachte. Seit dem wird eindringlich empfohlen
C<my> anstatt C<local> zu verwenden. Wenn n�mlich aus einem Block (in dem eine
C<local>-Variable definiert wurde) eine Routine aufgerufen wird, �berschreibt die
"lokale" Variable dort gleichnamige Br�der. Da das selten den Erwartungen entspricht,
sollte man es mit C<my> oder C<state> verhindern. Eine so erzeugte, lexikalisch
lokale Variable w�re innerhalb der Subroutine unbekannt und wird nur eingeblendet,
w�hrend ein Befehl des betreffenden Blocks ausgef�hrt wird. Einziger Unterschied
zwischen beiden Befehlen: C<my> initialisiert die Variable nach jedem Neustart des
Blocks von neuem mit dem Anfangswert, wohingegen C<state> da� nur beim ersten Mal
tut und sich danach den Inhalt bis zum n�chsten Blockaufruf merkt. Dieses Verhalten
von C<state>-Variablen nennt man in der Informatik "Closure" und ist seit Jahren
Bestandteil funktionaler Programmierung. Es lie� sich auch mit einem zus�tzlichen,
umschlie�enden Block und C<my> simulieren, was aber umst�ndlich und fehleranf�llig
war. F�r einfache Closures wurde deshalb der C<state>-Befehl von Perl 6 nach 5.10
zur�ckportiert.

Einfache Ausf�hrung mit R�ckhand

In Bl�cken stehen meist mehrere Befehle, von denen der zuletzt ausgef�hrte den
R�ckgabewert des Blocks angibt. Um ihn ihn einer Variable zuzuweisen, mu� der
Befehl C<do> verwendet werden, auch wenn nackte Bl�cke immer ausgef�hrt werden,
solang sie keine Parameter an eine Routine sind. (wie bei map, sort etc.)

Perl 6:
my $zahl = do { rette_regenwald(); gl�ckszahl() }
# oder l�nger :
my $coderef = { rette_regenwald(); gl�ckszahl() }
my $zahl = $coderef();

Auch auf einzelne Befehle, die nicht von geschweiften Klammern umgeben sind, kann
C<do> angewendet werden, um sie als Ausdruck zu evaluieren. Daher ist auch diese
umst�ndliche Umschreibung des tern�ren Operators m�glich.

Perl 6:
$x = do if $a { $b } else { $c };
$x = $a ?? $b :: $c; # k�rzer

Da C<do> einen Block immer genau einmal ausf�hren l��t, werden solche Abschnitte
jetzt auch "do once loop" genannt. Um mit dieser einfachen Regel nicht zu brechen,
wurden Schleifenkonstrukte wie C<do { ... } while ()> in C<repeat { ... } while ()>
umbenannt. Auch alle weiteren Verwendungsm�glichkeiten von C<do> die Perl 5 kennt,
wurden gestrichen. Daf�r f�hrte Larry mit C<gather> eine Variante von C<do> ein,
die einem Block erlaubt eine Liste zur�ckzugeben. Die einzelnen Werte werden mit
dem Befehl C<take> einzeln oder in Gruppen ausgew�hlt.

Perl 6:
# enth�lt: 'Gauguin', 'Cezanne'
my @maler = gather {
my $r = take 'Gauguin';
# $r enth�lt 'Gauguin'
take 'Cezanne' if $r;
}
# enth�lt: 1,10,2,20
my @x = gather for 1..2 {
take $, $ * 10;
}

In Kombination mit Schleifen und bedingten Anweisungen entfaltet C<gather/take>
erst seine volle M�chtigkeit. Die Beispiele zeigen auch wie Blockmodifikatoren
ohne weitere Klammern kombiniert werden k�nnen, was eine Konsequenz der bereits
vorgestellten Regeln ist. Auch bei nachgestellten Konstrukten kann damit manche
Zeilen vereinfacht werden.

Perl 5:
{ print if $_ % 2 } for 0..10;

Perl 6:
.print if $_ % 2 for ^11;

Soll in Perl 6 ein Befehl (als Methode) auf $_ angewendet werden, sollte er mit
einem Punkt beginnen. Die genauen Hintergr�nde dieser Designentscheidung wurden
in der ersten Folge erl�utert. Doch zur�ck zu den R�ckgabewerten. So wie C<take>
sie f�r den umgebenden C<gather>-Block bestimmt, gibt es auch einen Befehl f�r
den skalaren R�ckgabewert, der gleichzeitig die Ausf�hrung des Blocks abbricht.
Dieser Befehl hei�t zur allgemeinen �berraschung nicht C<return>. C<return> ist
Subroutinen und artverwandten Funktionseinheiten vorbehalten, aus Gr�nden, die
in der n�chsten Folge genau beschrieben werden. Das schlichte Verlassen des
umschlie�enden Blocks wird von einem C<leave> ausgef�hrt. Soll ein bestimmter
Block beendet werden, so mu� dessen Anfang durch ein "Label" (LABELNAME:)
markiert sein, um mit C<LABELNAME.leave> aus ihm hinauszutreten. Daraus folgt
ebenfalls, da� Perl 6 die mittelalterlichen Freuden extensiver C<goto>-Benutzung
keineswegs einschr�nkt. Selbst ein fortraneskes "computed goto" kann unver�ndert
(wie in Perl 5) geschrieben werden.

Der mit C<leave> definierte R�ckgabewert kann aber noch mehr, als in eine Variable
zugwiesen zu werden. Er kann auch die Ausf�hrung von Bl�cken steuern, die mit
Auswahloperatoren verkn�pft sind.

Perl 6:
tu_was() && das_auch();
# ist andere Schreibweise f�r:
{ tuwas() } && { das_auch() };
# ist andere Schreibweise f�r:
{ tuwas() } and { das_auch() };

Doch C<&&>, C<||> und C<^^> wurden schon in Folge 2 behandelt und f�r die Ops
macht es keinen Unterschied ob sie die Ergebnisse von einzelnen Befehlen oder
ganzen Bl�cken verkn�pfen. Wirklich neu und speziell nur f�r Bl�cke sind hingegegen
C<andthen> und C<orelse>. Grundlegend entsprechen sie den Befehlen C<and> und C<or>.
Beendet der linke Block erfolgreich, l��t C<andthen> ebenfalls den rechten
ausf�hren. Allerdings, um die Kontextvariable C<$_> weiterhin benutzen zu k�nnen.
Sie wird in diesem Sonderfall sozusagen "Closure" zweier Bl�cke. C<orelse> eignet
sich vor allem zur Fehlerbehandlung. Ist der linke R�ckgabewert nicht positiv,
startet C<orelse> den rechten Block, in dem die Fehlermeldung (Sondervariable $!)
weiterhin bekannt ist.

Bedingte Ausf�hrung ist einfach

In der Regel werden jedoch bedingte Anweisungen mit den vertrauten Schl��elw�rtern
C<if>, C<elsif>, C<else> und C<unless> formuliert. Letzeres ist zwar eine Eigenheit
von Perl, die nicht mehr mit C<elsif> und C<else> kombiniert werden darf, aber die
Funktionsweise dieser 4 Befehle sollte keiner weiteren Erkl�rung bed�rfen.
Auch C<given>, C<when> und C<default> wurden mit ihrer Einf�rung zur Version
5.10 gen�gend vorgestellt. Vergessen wurde dabei manchmal lediglich, da� C<given> und
C<when> sich auch in anderen Zusammenh�ngen benutzen lassen. C<given> eignet sich
hervorragend umst�ndliche Ausdr�cke zu vereinfachen wie folgende:

Perl 6:
$tiefer.objekt.namensraum.var1 =
$tiefer.objekt.namensraum.var2 +
$tiefer.objekt.namensraum.var3;
# oder :
given $tiefer.objekt.namensraum
{ $.var1 = $.var2 + $_.var3 }

Da C<given> ebenso wie C<for> (es gibt kein C<foreach> mehr) die Kontextvariable
setzt, k�nnen C<when>-Klauseln auch in C<for>-Schleifen verwendet werden.

Perl 6:
for @beine {
print "Ist $_ "
when 1 { say "�berhaupt ein Tier?" }
when 6|8|10 { say "ein Insekt?" }
when > 20 { say "eine Raupe?" }
default { say "ein Zweibein"}
}

C<default> ist nur ein Alias auf C<when *> (oder $_ ~~ Whatever) und im Gegensatz
zu einem "C-switch" entfüllt auch das notorische C<break>, da ein implizites
C<break> jede C<when>-Klausel beendet. Wer mag kann aber auch jederzeit mit C<break>
die Klausel und den umgebenden "contextualizer"-Block manuell verlassen. Wenn nur
zur n�chsten Klausel gesprungen werden soll, dann ist C<continue> das Mittel der Wahl.
Noch weitere besondere Sprungbefehle gibt es f�r Schleifen.

Sch�ne Schleifen

Auch hier wurden die alten Standarts kaum anger�hrt. C<while> und C<until> blieben
unver�ndert, lediglich ein C<repeat { ... } while $a < 3;> ist gew�hnungsbed�rftig.
Ansonst wird der Ausdruck nach C<while> und C<until> (ebenso wie bei C<if> und C<unless>)
in den boolschen Skalarkontext evaluiert. Selbstverst�ndlich wird dann bei einem
Ergebnis von Bool::True (beziehungsweise Boole::False) der Block ausgef�hrt. <for>
ist jetzt ein reiner Array-Iterator und evaluiert entsprechend in den "lazy list"-Kontext.
Genau deshalb wird die zeilenweise Abarbeitung einer Datei jetzt so geschrieben:

Perl 6:
my $fh = open "aufsatz.txt";
for =$fh {
...

W�rde jemand C<while> anstatt C<for> nehmen, so lieferte C<=$fh> im Skalarkontext den
gesamten Dateiinhalt auf einmal ab. Bis dahin ist alles logisch, doch was geschah mit
der klassischen "C-Stil"-for-Schleife. Ihr Ausdruck wird eindeutig in den void-Kontext
evaluiert und deshalb bekam sie ein eigenes Schlüsslwort. Inhaltlich unterschiedliches
soll auch optisch unterscheidbar sein. Deswegen heißt die Allzweckschleife nun "Schleife"
(C<loop>). Da der leere Kontext immer das Selbe Ergebnis liefert, ist auch C<loop {...}>
(ein Synonym für C<loop (;;) {...}>) eine nettere Schreibweise für C<while 1 {...}>, daß
weiterhin möglich ist. Eine weitere gute Nachricht: die Sprungbefehle C<last> (verlasse
die Schleife), C<redo> (wiederhole den Durchgang) und C<next> (zur nächsten Iteration)
funktionieren jetzt einwandfrei. Bei C<do {...} while ();> hat hier Perl 5 immer noch
Probleme, denn das sind nachträglich eingeführte Konstrukte und keine echten Schleifen.
Wie C<leave> können auch diese Sprungbefehle mit Labelmarken wie z.B. C<LABEL.next;>
oder C<next LABEL;> kombiniert werden.

Blockparameter

Im letzten Beispiel fehlte noch ein entscheidendes Detail. F�r C<for>-Schleifen wird
oft eine lokale Laufvariable definiert:

Perl 5:
for my $i (1..10) { ...

Weil hier das C<my> nicht innerhalb des Blockes steht, f�r da� es seine G�ltigkeit
besitzt, w�rde das gegen die klaren Regeln in Perl 6 versto�en. Deshalb gibt es daf�r
nun einen besonderen Syntax der es gleichzeitig zul��t mehrere Parameter an einen
Block zu �bergeben. Da der Syntax symbolisieren soll, da� hier schnell ein paar Werte
in den Block "hineingeworfen" werden, nennt sich diese Formulierung "pointy block".
Sie erlaubt elegante L�sungen, f�r die bisher wesentlich kompliziertere Formulierungen
notwendig waren.

Perl 6:
for 1..10 > $i { ...
for %h.kv
> $k, $v { ...
for @a Z @b Z @c > $a, $b, $c { ...
while berechne()
> $wert { ...

Auch wenn C<while> in den boolschen Kontext evaluiert, bekommt C<$wert> den Betrag
den C<berechne()> liefert. Wie beschrieben sind diese Variablen Parameter und nicht
lexikalisch lokal und k�nnen daher auch nicht innerhalb des Blocks ver�ndert werden.
Braucht man jedoch unbedingt ver�nderbare Laufvariablen, so mu� dies mit einem leicht
ge�nderten Syntax gekennzeichnet sein.

Perl 6:
for 1..10 <-> $i { ...

Wenn kein Variablennamen angegeben w�re, landen die Inhalte nat�rlich in $_ oder @.
Nun ist @2 nicht immer der sch�nste Variablennamen und verwirrt manch einen Neuling.
F�r solche F�lle erfand Larry die automatisch benannten Parameter die auch einen
weiteren Sonderfall in Perl 5 mit einer allgemeinen Regel abschaffen. Damit ist der
C<sort>-Befehl gemeint, in dessen Block die einmaligen Sondervariablen $a und $b
bekannt sind, welche jetzt C<$^a> und C<$^b> geschrieben werden. Die Twigil (sekund�re
Sigil) "^" kennzeichnet die Platzhalter-Variablen. Diese d�rfen benutzt werden,
ohne ihre Namen zu deklarieren. Man mu� nur darauf achten, da� alphabetisch sortiert,
ihre Namen der Reihenfolge der Parameter entsprechen, in der sie dem Block �bergeben
wurden.

Perl 6:
for 1..10 { say $^i ...
for 1..10 { say $^cc ...
$coderef1 = { say $^a, $^b };
$coderef2 = { say $^z, $^zz };
$coderef1('a','d'); # sagt ade
$coderef2('a','d'); # dito

Mehr dazu in Teil 5, wenn in Subroutinen die hohe Schule der Parameter�bergabe
zelebriert wird.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial Part 3


Arrays in Perl

Und wie angekündigt geht es in dieser Folge um Operatoren, die den Umgang mit
Arrays erleichtern. Hier hat Perl 6 sehr zugelegt. Zwar zeichnet sich Perl 5
durch sehr einfach und vielseitig verwendbare Arrays aus, die zudem mit 'map',
'grep' und 'for' sehr flexibel bearbeitet werden können (das gibt es natürlich
auch alles weiterhin), aber besonders funktionale Sprachen wie Haskell haben in
den letzten Jahren gezeigt, daß auf dem Gebiet weit mehr möglich ist. Und es war
auch die durch Pugs herbeigeführte, enge Zusammenarbeit von Haskell-Programmierern
und dem Design-Team, die auf diesem Gebiet deutliche Spuren im Perl 6-Syntax
hinterlassen hat. Darüber hinaus hat sich ebenso grundlegendes in der Schreibweise
der Arrays geändert, daß damit nichts zu tun hat.

Als Perl noch klein war und auf Larrys Wickeltisch lag, gab es nur einzelne Werte
und einfache Listen oder Hashes als Spielzeug. Um sie gut unterscheiden zu können,
bekamen sie als Erkennungssymbol die Sigil $, @ und %. Da aber Arrays und Hashes
auch einzelne Werte abliefert können und Hashes sogar Arrays von Werten, entschied
Larry, es sei besser in diesen Fällen die Sigil zu ändern, um immer zu sehen, ob
man es grad mit einen oder mehrere Werten zu tun hat. (Hashes sind ja in Perl 5
bloß Arrays mit gerader Länge.) Mit 7 Jahren erhielt Perl jedoch zur Einschulung
Referenzen. Das war gut, denn nun konnte es komplexe Datenstrukturen erzeugen,
was für größere Schulaufgaben wichtig ist. Aber damit verloren die veränderlichen
Sigils ihren ursprünglichen Sinn, denn "$fibo[7]" konnte nun auch (eine Referenz
auf) einen Array oder einen Hash zurückgeben. Und auch das Dereferenzieren mit
geschweiften Klammern wie bei "@a = @{$fibo[7]}" kann zu komplexen Ausdrücken
führen, die Anfänger verwirren. Deshalb haben Variablen in Perl 6 unveränderliche
Sigils. Zu deutsch: eine Arrayvariable beginnt immer mit einem '@'. Damit bleibt
wenigstens der Typ der Variable sichtbar. Zusätzlich vereinfacht das Dinge wie:

Perl 5: Perl 6:
@gerade = @{$zahlen[4]}; @gerade = @zahlen[4];
$zahl = $halde->345; $zahl = $halde[3;4;5];

Diese Beispiele zeigen: den Kontext bestimmt jetzt vor allem der Empfänger der
Daten. Erhält ein Array eine Arrayref, dereferenziert Perl 6 automatisch. Weist
man einem Array einen Skalar zu, hat er nur noch einen (neuen) Wert. Wird einem
Skalar ein Array zugewiesen, so wird er zur Arrayref (ich mein natürlich Capture),
die behandelt werden kann wie ein Array, auch wenn sie die Sigil $ hat. (Fast wie
ein Array in PHP.)

In Teil 2 wurde vorgestellt, daß ebenso Operatoren den Kontext bestimmen können.
Auch hier erzwingt "~" den Stringkontext, was der Aneinanderreihung der Inhalte
aller Elemente entspricht (selbstverständlich durch Leerzeichen getrennt). Und das
"+" fordert den numerischen Kontext, in dem ein Array die Anzahl seiner Elemente
liefert, in gleicher Weise wie Perl 5-Arrays im skalaren Kontext. Dafür gibt es
noch einen objektorientierten, expliziteren Syntax:

Perl 5: Perl 6:
scalar @gerade; +@gerade; # Elementenanzahl
scalar @gerade; @gerade.elems; # geht auch
$#rray; @rray.end; # letzter Index

Doch auch für das "~@a" wurde noch eine Schreibweise geschaffen, da innerhalb von
doppelten Anführungsstrichen Arrays und Hashes nicht automatisch expandieren. Dies
soll eine einfache Ausgabe von Mailadressen und Prozentangaben ermöglichen.

Perl 6:
say @namen;
say "meine Adresse: ich@namen.de";

Möchte man die Ausgabe der oberen Zeile innerhalb einer Angabe geht das nun auch
so:

Perl 6:
say "Die taoistischen Unsterblichen sind: @namen[]";

Auch die Ausgabe einzelner Arraywerte kann Umsteiger anfangs ratlos machen, denn
z.B. das vorletzte Element fordert man jetzt mit "@a[*-2]" an. Der Stern bedeutet
jetzt in fast jedem Kontext so etwas wie "alles", "unendlich", "irgendetwas" oder
"Grenzwert". Damit möchte ich aber die Einweisung in Perl 6-Arrays beenden, da
eigentlich die Operatoren das aktuelle Thema sind.

Füttern der Arrays

Das einfache füllen eines Arrays geht wie gewohnt:

Perl 6:
my @noten = ('Do', 'Re', 'Mi', 'Fa', 'Sol', 'La', 'Si');

Die runden Klammern können weggelassen werden, da das Komma jetzt arrayerzeugender
Operator ist. Sonst würde im nächsten Beispiel gerade mal "Si" in "@noten[0]" landen.

Perl 6:
my @noten = 'Re', 'Mi', 'Fa', 'Sol', 'La', 'Do', 'Si';

Aber so würde es wohl auch kein geübter Perlautor schreiben, denn es gibt ja qw().
Gibt es das noch? So etwas praktisches und nützliches wurde nicht nur behalten,
sondern auch in der Schreibweise vereinfacht.

Perl 6:
my @noten = <Re Mi Fa Sol La Do Si>;
my @tiere = << $baer $hase $igel >>;

Im vorigen Teil hatte ich ja schon erwähnt das es von "<>" noch eine Version gibt,
die interpoliert. Die Namensgebung lehnt an '' und "" an, wo auch die "doppelte"
Version interpoliert (Variablennamen mit ihren Inhalt ersetzt). Diese Schreibweise
ist auch praktisch wenn dem Array Werte eines Hashes zugewiesen werden.

Perl 6:
my @ausgaben = %rechnung< alfred hein peter >;
my @ausgaben = %rechnung<< @namen[] >>;

Hashschlüssel wurden zwar vorher auch oft nicht gequotet aber sowas tun nur Rüpel,
da barewords bekanntermaßen böse und hinterhältig sind und liebe Programmierer
immer strict verwenden. In Perl 6 hat man hier keine Wahl, da strictness default
ist, und es keine Bareword-Stringliteral mehr gibt.

Auch die altbekannten Rangeoperatoren kann man immer noch zum befüllen von Arrays
verwenden:

Perl 6:
my @ziffern = 0 .. 9; # 0,1,2,3,4,5,6,7,8,9
my @buchst ='a'..'z'; # <a b c d e f ... y z>

Ersteres kann man aber jetzt noch kürzer schreiben:

Perl 6:
my @einer = ^10; # 0,1,2,3,4,5,6,7,8,9

Ja genau, das Dach schließt das ihm Folgende als Grenzwert aus und die 0 wird
standartmäßig als untere Schranke angenommen. Dieses Konstrukt hat vor allem zwei
praktische Anwendungen. Im Skalarkontext kann man damit Bereiche definieren, deren
Schranken nicht zur Menge gehören, aber auch einfache for-Schleifen können so
noch um ein paar Anschläge kürzer geschrieben werden:

Perl 6:
3 ~~ 3^.. 7; # gibt Bool::False
for ^5 { ... } # iteriert 5 mal

Und da wir grad bei Schleifen sind: Wie oft wollten Perl 5-Programmierer die
Iteratorvariable in Sprüngen oder rückwärts zählen lassen. Dies geht nun alles
sehr einfach:

Perl 6:
for ^10:by( 2) {...}; # 0,2,4,6,8
for 4..1:by(-1) {...}; # 4,3,2,1

Selbstverständlich ist "by" kein Spezialbefehl für Schleifen, sondern kann immer
für Bereiche (Ranges) eingesetzt werden. Wem es überhaupt nicht gefällt, kann im
zweiten Beispiel auch auf reverse zurückgreifen, denn '4..1' evaluiert zu einer
leeren Menge oder einem leeren Zahlenbereich.

Perl 6:
for reverse 1..4 {...}; # 4,3,2,1

Es lebe die Faulheit

Die Menge die ein Perl 6-Array speichert, kann aber nicht nur leer sein, sondern
auch unendlich groß. Dafür braucht es nicht einmal unendlich viel RAM. Es reicht
vollkommen ihn wie folgt als unendlich zu bestimmen:

Perl 6:
@zahlen = 5 .. Inf; # von 5 bis Unendlich
@zahlen = 5 .. ; # das Selbe
@zahlen =
.. *; # aka -Inf .. +Inf

Inf (gibt es in den Varianten +Inf und -Inf) oder * steht hier für Unendlich und
es ist auch ein mathematisch korrektes Inf, daß Perl 6 zurückgibt wenn man eine
Division duch 0 versucht. Aber wie funktionieren die letztgenannten Beispiele?
Perl 5 hätte hier unsanft angemeldet "Range iterator outside integer range at ...",
Perl 6 expandiert jedoch den Array erst wenn er tatsächlich gebraucht wird und
dann auch nur bis zu dem benötigten Element. Diese "lazy evaluation" gehört zu den
Einflüssen funktionaler Sprachen wie Haskell in Perl 6, die ich zu Begin ansprach.
Sie bewirkt, daß eine "for"-Schleife, die über "@zahlen" iteriert, so lange läuft,
bis ein Sprungbefehl das Verlassen der Schleife erzwingt oder das Betriebsystem
aufhört zu arbeiten. Doch im Gegensatz zu Haskell gilt für Perl immer noch TIMTOWTDI.
Wer seine Listen sofort expandiert haben möchte, kann dies mit "eager" erzwingen.

Perl 6:
@zahlen = eager 5..*; # jetzt hängt das programm wirklich
$zahl = lazy "$text:" ~ berechne();

Im Gegenzug ist es auch möglich mit "lazy" die "lazy evaluation" zu erzwingen, wo
Perl per default "eager" evaluiert, wie bei einer Zuweisung in den Skalarkontext.
Es empfiehlt sich jedoch dabei aufzupassen, da die Variable oder die Routine in
dem Beispiel zu einem späteren Zeitpunkt andere Werte liefern können. Überhaupt
erfordert die "lazy evaluation" etwas mehr Aufmerksamkeit um genau zu erkennen,
wann etwas ausgeführt wird. Das folgende Beispiel filtert die numerisch geraden
Zahlen aus einem Array und überweist sie in einen zweiten Array. Der verwendete
feed-Operators dient der Weiterreichung von Arrayelementen um etwa Schwartz'sche
Transformationen und ähnliches explizit zu formulieren. Neu daran ist, daß die
Werte einzeln "@out" verlassen um sogleich das ganze Fließband an Befehlen zu
passieren und im "@in" zu verschwinden. Im dritten Beispiel würde die gerade Zahl
also zuerst halbiert und in "@in" gespeichert, bevor der nächste Wert aus "@out"
entnommen wird. Diese häppchenweise Arbeitsweise gab den feed-ops ihren Namen.
Das vierte Beispiel demonstriert, daß sie von links nach rechts zuweisen können
und daß sich die Taktstraße auch aus mehreren Quellen speisen kann. Wegen der
Schreibweise "==>>" folgt dem letzten Element aus "@out1" das erste Element aus
"@out2".

Perl 6:
@in = grep { $%2 } <== @out;
@in = grep { $%2 }, @out; # funktionsgleich
@in = map {$_ / 2} <== grep { $_%2 } <== @out;
@out1 ==>> @out2 ==>> grep { /\s/ } ==> @in;

Selbstverständlich unterbricht ein "eager" oder "sort" die "lazy evaluation" in
solchen Befehlsfolgen.

Befehle wie push, pop, shift und splice, die auch dafür geeignet sind, Arrays zu
füttern, werden bereits genügend in der perldoc beschrieben. Bis auf Nuancen hat
sich für diese Befehle nichts geändert, weswegen sie hier nicht erklärt werden.

Die Raubtierfütterung geht weiter

Wenn einem grad nichts einfällt, kann ein Array natürlich auch mit Wiederholungen
eines Wertes gefüllt werden:

Perl 6:
@affen = "gibbon" xx 5;
# mir ist doch noch was eingefallen
@affen = <gibbon makake> xx 3;

Anders als in Perl 5.10 unterscheidet "x" hier keinen Kontext. Es wiederholt, wie
in der vorigen Folge gezeigt, immer einen String und erzeugt einen meist längeren
String. "xx" wiederholt immer eine Liste, selbst wenn sie nur ein Element hat.
Die Ergebnisse werden dann auch, wie anfangs beschrieben, an den Kontext des
Empfängers angepasst. Was Perl 5 leider auch nicht kennt, ist das große "X", der
Kreuzoperator. Ohne Schleifen und Zusatzmodule können damit alle Kombinationen
der Elemente mehrer Arrays erzeugt werden. Wichtigste Regel ist hier dabei, daß
der linke Array vorrang hat, also zuerst alle Paare mit dem ersten linken Element
gebildet werden, dann mit dem zweiten usw.

Perl 6:
1,2 X 3,4 # (1,3),(1,4),(2,3),(2,4)

Wie das Resultat aufgelöst wird, hängt wieder vom Kontext ab, wobei es an der Zeit
ist, einen neuen, interessanten Kontext vorzustellen. Der "slice"-Kontext, erkennbar
an seiner Sigil "@@", verweist auf Arrays, die wiederum Arrayreferenzen enthalten.
Slice bedeutet soviel wie Arraystück oder Scheibe. Der einfache Arraykontext würde
einen flachen Array erzeugen. Oft möchte man das gerade nicht.

Perl 6:
$ 1,2 X 3,4 # \(1,3),\(1,4),\(2,3),\(2,4)
@ 1,2 X 3,4 # 1,3,1,4,2,3,2,4
@@ 1,2 X 3,4 # 1,3,1,4,2,3,2,4

Genauso kontextsensitiv ist auch der "zip" Operator, der ebenfalls aus bestehenden
Arrays neue zusammenstellen kann. Er wird "zip" oder "Z" geschrieben und aus der
zweiten Schreibweise läßt sich auch sein Vorgehen ableiten. (Man nehme zuerst ein
Element aus dem linken Array, dann das erste aus dem rechten. Dann wieder zum
ersten und zurück ... ). Zip heißt zu deutsch Reißverschluss, was den Mechanismus
auch gut beschreibt.

Perl 6:
$ 1,2 Z 3,4 # \(1,3),\(2,4)
@ 1,2 Z 3,4 # 1,3,2,4
@@ 1,2 Z 3,4 # 1,3,2,4
% 1,2 Z 3,4 # { 1 => 3, 2 => 4 }

Besonders für die parallele Verarbeitung mehrer Arrays in einer Schleife ist das
sehr praktisch. Dies zeigt allerdings die nächste Folge des Tutorials, weil es
noch Wissen zu den inneren Verhaltensweisen anonymer Blöcke verlangt, was weit
außerhalb des heutigen Themas liegt.

Sowohl der Kreuz- als auch der Reißverschlussoperator kann mehrfach verkettet
werden, die Ergebnisse werden lediglich entsprechend komplexer.

Perl 6:
1,2 X 3,4 X 5,6 # (1,3,5),(1,3,6) ...

Giga, Hyper, Meta ...

Mit dem großen Kreuz lässt sich sogar noch weit mehr anstellen wenn es circumfix
(umschließend wie Klammern) um einen anderen Operator gesetzt wird. Dies eröffnet
das völlig neue Feld der Metaoperatoren für Arrays. Operatoren für Skalare können
somit auf Listen angewendet werden und für die Fülle der neuen Möglichkeiten muß
niemand lange Listen an neue Befehlen lernen. Es reicht völlig die Arbeitsweise
der drei Metaoperatoren zu verstehen.

Der Kreuz-Metaoperator funktioniert auch fast wie der einfache Kreuzoperator. Auf
jedes Kombinationspaar wird lediglich der rechte Operator angewendet. Am Beispiel
ist das leicht nachvollziehbar:

Perl 6:
<a b> X~ <1 2> # <a1 a2 b1 b2>
<1 2 3> X* <2 3> # <2 3 4 6 6 9>

Der praktische Nutzen von solchem Code ist nicht nur daß er mindestens 5 Zeilen
Perl 5 ersetzt, sondern daß er dem Interpreter auch erlaubt die Arbeitsschritte
auf mehrere Prozessoren(kerne) zu verteilen, da es auch nicht darauf ankommt in
welcher Reihenfolge die Werte berechnet werden, solang sie in der erwarteten
Reihenfolge ankommen. Genau diese Parallelisierung der Datenverarbeitung erlaubt
auch der nächste Metaop: der Hyperoperator. Er besteht aus zwei ">" oder "<", kann
aber auch mit den UTF-Symbolen namens Chevron geschrieben werden, welche genauso
aussehen. Wie zu erwarten, war das Gegenstand kräftiger Diskussionen, während der
Larry Wall seinen Standpunkt verteidigte: "UTF ist schon lange Standart und man
solle die Verwendung von UTF wenigstens ermöglichen. Alle "UTF-Operatoren" sind
ohnehin optional und Perl 6-Quellcode wird intern eh immer als Unicode angesehen."

Der klassische Anwendungsfall für Hyperoperatoren besteht darin, die ersten Werte
zweier Arrays als Operanden zu verwenden und das Resultat als ersten Wert im
Ergebnisarray zu speichern. Nächste Operanden sind die jeweils zweiten Werte u.s.w.
Sind beide Arrays unterschiedlich lang wird mit leeren Werten aufgefüllt

Perl 6:
@reste = <5 3 7> >>%<< <2 7 5>; # <1 3 2>
@summen = <2 3 4> >>+<< 1; # <3 3 4>

Möchte man einen einzelnen Wert auf eine Liste anwenden, so reicht es, die Richtung
des Hyperoperators umzukehren.

Perl 6:
<5 3 7> >>+>> 1; # <6 4 8>

Das breite Ende der Pfeile zeigt an, welche Seite die Dimension des Ergebnises
bestimmt. Hätte das letzte Beispiel Variablen benutzt, ließe es sich aber noch
elegant verkürzen.

Perl 6:
@a = @a >>+>> 1;
@a >>+=>> 1; # dito
@a >>++; # noch kürzer

Sehr nützlich ist auch, daß Hyperoperatoren sehr gut mit mehrdimensionalen Arrays
umgehen können.

Perl 6:
-« [1, 2, 3] # [-1, -2, -3]
[1, 2, 3] »+» 4, 5, 6 # [5, 7, 9]
[1, 2, 3] «+» 4, [5, 6]
# == [1,2 «+» 4, 3 «+» 5, 6] == [5, 6, 8, 9]

Im zweiten Beispiel bestimmt die linke Seite, wegen der Richtung der Operatoren,
die Dimensionalität. Beim dritten ist es keine Seite und so wird auch jedes slice
als Element angesehen und entsprechend "ausmultipliziert".

Die dritte Klasse an Metaoperatoren sind die Reduktionsoperatoren. Ihr Name ist
redlich verdient, da sie die Dimension ihres Operanden reduzieren, oder zu deutsch:
Sie machen aus einem Array einen Skalar, indem sie alle Arrayelemente miteinander
anwenden. Reduktionsoperatoren werden in eckigen Klammern geschrieben.

Perl 6:
+ 1..4 # 10 = 1+2+3+4
~ 'a'..'f' # 'abcdef'

Trickreich und effektiv lassen sie sich auch mit Vergleichs- und Auswahloperatoren
kombinieren.

Perl 6:
$sorted = < @a # wahr wenn sortiert
$wert = || @a # erster nicht leerer Wert im Array
$min = min @zahlen # @zahlen.min ginge auch
$min, $max = minmax @zahlen

Aber auch Reihen, wie man sie beim Mathe-Wettbewerb "Euler" häufig braucht,
lassen sich mit einer weiteren Schreibweise der Reduktionsoperatoren sehr knapp
formulieren.

Perl 6:
\* 1..3 # 1, 2 = 1*2, 6 = 2*3
\+ ^10 # 0,1,3,6,10,15,21,28,36,45

Diese Form liefert die Aufzählung aller Zwischenergebnisse eines Reduktionsops.
Formal hieße sich das ungefähr als "@ergebnis[$n] = @ergebnis[$n-1] op @input[$n]"
ausdrücken. Und auch an dieser Schreibweise erkennt man die Regel einer Huffman-Kodierung
(Je seltener etwas menötigt wird, desto länger (seltsamer) ist sein Name).

Und noch ein Blick in die Quantenwelt

Neben Larry Wall ist Damian Conway einer der Hauptgestalter der Perl 6-Syntax.
Nicht nur die ableitbaren Grammatiken der "rules" basieren zu einem guten Teil
auf seinem "Parse::RecDescent", auch sein Modul "Quantum::Superpositions" ließ
viele Perl-Programmierer erste Bekanntschaft mit Junctions schließen. Wie vieles
wofür sich Damian begeistert, können auch Junctions als abgehoben und schwer
zugänglich erscheinen. In funktionalen Sprachen wie Haskell erwiesen sie sich
jedoch als hilfreich, um komplexe logische Verknüpfungen einfacher zu formulieren.
Würde man in Perl 5 z.B. noch schreiben:

Perl 5:
if ($farbe eq "rot" or $farbe eq "blau" or $farbe eq "grün") {
print "Sie wählten eine Grundfarbe, interessant.\n"
}

So geht das in Perl 6 auch junktiv.

Perl 6:
if $farbe eq "rot" | "blau" | "grün" {
say "Sie wählten eine Grundfarbe, interessant."
}

Aus Perl 5 ist bekannt, daß "|" für ein logisches "oder"(or) steht, "&" für "und"
(and) und "^" für "entweder oder" (xor) steht. Neu ist, daß man den Sachverhalt:
"rot oder blau oder grün" in einer Variable speichern kann. Das letzte Beispiel
ginge damit so:

Perl 6:
my $gfarben = "rot" | "blau" | "grün";
if $farbe eq $gfarben ...

Wem das nicht ganz geheuer kann auch einen vertrauten Array verwenden und dessen
Elemente junktiv verknüpfen.

my @gfarben = <rot blau grün>;
if $farbe eq any(@gfarben) ...

Sicher ließe sich anstatt "any(@gfarben)auch die reduktive Variante @gfarben"
verwenden, allerdings ist ersteres weit eingängiger (und aus Quantum::Superpositions
bekannt). In gleicher Weise entspricht das "alleinem" (and), "onedem"
(xor) und "none" einem "not".

Perl 6:
if all(@tiere) ~~ all(@affen) { say "nur affen hier ...
if all(@tiere) ~~ all(@affen) | $faultier { ...
if schau() | vergleich() eq any(@gfarben) { ...

Selbstverständlich lassen sich Junctions auch schachteln. Mit ihnen kann man auch
Operationen auf mehrere Werte parallel anwenden, ähnlich den Hyperoperatoren.

Perl 6:
my $gfarben = "rot" | "blau" | "grün";
$gfarben ~= 'e'; # "rote" | "blaue" | "grüne";
(1|2) + (3&4); # (4|5) & (5|6)

Am schönsten daran ist wohl, daß man so komplexe logische Regelwerke kombinieren
kann und Perl alle Details überläßt.

Danke

Über Arrays in Perl 6 lassen sich noch viele weitere Neuerungen berichten. Dieses
mal ging es vor allem um die damit zusammenhängenden Operatoren. In der nächsten
Folge lautet das Thema Kontrollstrukturen. Es wird daran anknüpfen, da Schleifen
oft für die Bearbeitung von Arrays und Hashes eingesetzt werden.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial Part 2


Perl is an operator based language

Unlängst konnte ich herzlich lachen, als ich diesen Satz in der p6l-Mailingliste
sah. Der wahre Kern dieser Pointe besteht darin, daß Perl 6 tatsächlich auffallend
viele Operatoren besitzt und das Operatoren einen bedeutenden Anteil an Perl's
Ausdrucksstärke haben. Und weil Operatoren bereits mit wenig Quellcode angewendet
werden können, eignen sie sich vortrefflich als Einstieg in die Tiefen der Sprache.
Letztlich ist ihre Kenntnis auch eine wichtige Vorraussetzung für spätere,
umfangreichere Beispiele.

Ein wenig Numerik zum aufwärmen

Beginnen wir ganz einfach mit den geläufigsten Operatoren, den Grundrechenarten
('+', '-', '*'), die jeder Insasse dieser Zivilisation vom Grundschullehrer nahe
gebracht bekommt.

Perl 6:
say 3 + 4; # 7
say 3 - 4; # -1
say 3 * 4; # 12

Ebenso zeigte euch der freundliche Mathelehrer auch die Division, Modulo (Rest
einer ganzzahligen Division) und Potenzierung, nur werden die in Computersprachen
etwas anders geschrieben als im Unterricht, da man hier in den alten Tagen auf
den ASCII-Zeichensatz beschränkt war.

Perl 6:
say 3 / 4; # 0.75
say 3 % 4; # 3 ( 3 div 4 = 0 Rest 3 )
say 3 ** 4; # 81 ( = 3 * 3 * 3 * 3 )

Algebra unter erschwerten Bedingungen

So weit erfüllen die Ergebnisse die Erwartungen eines Mathematikers und zeigen,
daß @larry noch wissen, welche Dinge sie besser nicht ändern. Was Mathematiker
jedoch nicht erwarten, aber sehr wohl Perl-Programmierer kennen, die mit Daten
zweifelhafter Herkunft hantieren, sind Zahlenwerte, die Buchstaben enthalten
können. Diese übergeht Perl großzügig, in dem es alles ab dem ersten Zeichen
ignoriert, daß nicht als Zahl interpretiert werden kann. Das wird
Überführung eines Wertes in den numerischen Kontext genannt und geschieht immer
dann, wenn Perl anhand der verwendeten Operatoren erkennt, daß hier mit Zahlen
gerechnet wird. Folgende Beispiele geben gleiche Ergebnisse unter Perl 5 und 6
aus. Die Kommanotation von 'print' hab ich deswegen gewählt, damit die Beispiele
wirklich unter Perl 5 und 6 laufen, da Strings in beiden Sprachen unterschiedlich
zusammengefügt werden.

Perl 5 & 6:
print 3 * '2.2j4',"\n"; # 6.6, da 3 * 2.2
print 3 * 'b2.2j4',"\n"; # 0, da 3 * 0
print - '2.2ah',"\n"; # -2.2, einfache Negierung

Die Unterschiede beginnen, wenn man das '+' als Präfixoperator benutzt.

Perl 5 & 6:
print + '2.3ah',"\n";
print int '2.3ah'; # 2
print abs '-2.4ah'; # 2.4

Während Perl 5 dieses '+' ignoriert, überführt es in Perl 6 den folgenden String
in den numerischen Kontext, wie das '-' in Perl 5 und 6, nur ohne zu negieren.
Zwar gab es bereits in Perl 5 'int', welches ganze Zahlen zurückgibt und 'abs'
das positive Zahlenwerte liefert, aber eine einfache und direkte Umwandlung in
den numerischen Kontext gab es erstaunlicherweise bisher nicht.

Noch mehr Numerik zum vertiefen

Perl 6:
say + '2.3ah'; # 2.3
say - '2.0ah'; # - 2

Diese Beispiele sind es wert, längere Zeit meditativ betrachtet zu werden. Denn
sie beschreiben eine grundlegende Arbeitsweise vieler Operatoren. Perl 6 kennt
nämlich nicht nur Skalar, Array und Hashkontext, sondern noch viele mehr, wie
zum Beispiel den numerischen Kontext. Dessen Symbol ist quasi das '+', welches
diesen nicht nur erzwingt sondern auch Sigil für eine Reihe von Operatoren ist,
die ebenfalls diesen Kontext forcieren. Sie sind bereits als bitverändernde Ops
bekannt, aber äusserlich kaum wiederzuerkennen. Die guten, alten Shiftoperatoren
werden jetzt z.B.'+<' und '+>' geschrieben, wie das nächste Beispiel zeigt. Das
sieht zuerst ungewohnt aus, ist aber logisch, weil meist nur der numerische Wert,
den eine Zeichenkette ausdrückt, für Bitoperationen gebraucht wird. Ausserdem sind
'<<' und '>>' jetzt interpolierende Schwestern der '<' und '>'-Klammern geworden.
So wie "" interpolierende Schwestern von '' sind. Analog zum Erklärten sind die
bitweisen 'und'(and), 'oder'(or) und 'entweder-oder'(xor) nun '+&', '+|' und '+^'.
Die einfachen logischen Operatoren '&', '|' und '^' sind jetzt junktiv. Das werde
ich genauer im Teil 3 erklären, weil es auch den Umgang mit Arrays berührt.

Perl 6:
say 5 +< 2; # 20; 10100 = 101 << 2
say 5 +> 2; # 1; 001 = 101 >> 2
say 5 +& 3; # 1; 001 = 101 & 011
say 5 +| 3; # 7; 111 = 101 | 011
say 5 +^ 3; # 6; 110 = 101 ^ 011

Die meisten sonstigen numerischen Operatoren bleiben wie bekannt, lediglich das
Autoincrement (++) und Autodecrement (--) sind nun keine reinen numerischen Op's
mehr. Bevor ihr einen Herzschlag bekommt: klar verändern sie Zahlenwerte um 1.
Aber sie erzwingen keinen Kontext, sondern passen sich ihm an. Denn sie sind
allgemeine Operatoren geworden, die den Vorgänger oder Nachfolger eines Wertes
rufen und bedienen sich daher des gleichen Mechanismus wie einige Vergleichsops.
Ähnlich brechnen auch 'div' und 'mod' (auf Zahlen angewendet) Division und Modulo,
können sich aber einem anderen Kontext auch anpassen.

Perl 6:
my $a = 5;
say ++$a; # 6 (5 + 1)
say $a++; # 6 (erhöht wird danach)
say --$a; # 6 (6 + 1 - 1)
say $a--; # 6 (verringert wird danach)
say $a --; # Error

Routinierte Perlprogrammierer sollten nicht vergessen, daß jetzt zwischen Variable
und ihrem Präfix- oder Suffixoperator kein Leerzeichen stehen darf. Die Gründe
hab ich im vorigen Teil des Tutorials dargelegt.

Operatoren des Stringkontext

Analog zum numerischen gibt es, wie erwarted, auch einen Stringkontext, dessen
Symbol '~' ist. Warum die Tilde? Nun, weil der ultimative Textoperator '=~' hieß,
also Tilde schon immer etwas mit Strings zu tun hatte und der Punkt, wie letzes
mal schon gezeigt, nun die Objektorientierung ausdrückt. Die Tilde wandelt also
in den Stringkontext um und kann auch Strings verknüpfen, wie der Unixbefehl 'cat',
den es jetzt auch innerhalb von Perl gibt.

Perl 6:
say ~ '2.3ah'; # '2.3ah'
say ~ '2.0'; # '2.0'
say ~ 0xff; # 255
say 'rot ' ~ 'blau' # 'rot blau'
say cat('rot ', 'blau'); # dito
say 'Farbe:' ~ color(); # eine Testausgabe

Die Tilde kann aber auch genauso mit den bitveränderndem Operatoren kombiniert
werden, wie im numerischen Paralleluniversum das Plus. Auch wenn ich bis heute
keinen praktischen Nutzen, für die daraus entstehenden Operatoren gefunden habe,
will ich nicht ausschließen das es einen gibt. Die Tilde signalisiert, daß die
Werte zeichenweise in Ordinalzahlen umgewandelt werden (mit 'ord'). Auf die so
entstandenen Reihen numerischer Werten, können nun die bitweisen Rechenarten
problemlos angewendet werden. Lediglich beim shiften muß der rechte Operand eine
Zahl sein. Die Ergebnisse werden dann selbstverständlich vor der Ausgabe wieder
mit 'chr' in Textzeichen umgewandelt.

Perl 6:
'-' ~< 1 ; #'Z'= chr 90 ( 45 << 1 )
'z' ~> 1 ; #'='= chr 61 ( 122 >> 1 )
'a' ~& 'D'; #'@'= chr 64 (1100001 & 1000100)
'a' ~| 'D'; #'e'= chr 101 (1100001 | 1000100)
'a' ~^ 'D'; #'%'= chr 37 (1100001 ^ 1000100)

Perl 6 kennt aber erfreulicherweise auch die bekannten Befehle zur Bearbeitung von
Zeichenketten wie 'index', 'substr', 'uc', 'lc' und so fort und sogar rubysche
Spielereien wie reverse, welches die Reihenfolge der Zeichenkette umkehrt. Genau
genommen sind das aber eingebaute Funktionen und keine Operatoren, um die es hier
geht. Die bestehen aus wenigen, nicht alphanumerischen Zeichen. Obwohl, das
stimmt eigentlich auch nicht, denn Perl 6 kennt ebenfalls noch das kleine 'x',
mit dem Perlschreiber schon früher Strings vervielfältigten.

Perl 6:
'a' x 5; # 'aaaaa'
'so' x 3; # 'sososo'

Bei wirklich komplexen Fällen, führt jedoch kein Weg an den regulären Ausdrücken
vorbei! Die nennen sich jetzt rules und bieten derart viele Möglichkeiten, daß
sie hier mindestens einen eigenen Teil benötigen. Deswegen folgen jetzt nur wenige
Beispiele um den in Perl 5.10 eingeführten Smartmatchoperator zu demonstrieren.
Dieser ersetzt in Perl 6 das '=~' vollständig.

Perl 6:
"Ich hab getanzt ..." ~~ m/tanz/; # 1
"Ich hab getanzt ..." !~~ m/wanze/; # 1
"Ich hab getanzt ..." ~~ s/tan/schwit/; # Error

Im Gegensatz zu Perl 5 liefert er aber nur einen boolschen Wert zurück, der den
Erfolg der Suche meldet. (Auch beim Ersetzen.) '!~~' negiert am Ende der Suche
lediglich diesen Ergebniswert. Jede weitere Information zum Suchvorgang können
Programmierer wie gewohnt den Spezialvariablen entehmen, deren Syntax sich auch
geändert hat. Die Fehlermeldung im Beispiel ist der Tatsache geschuldet, daß nur
in Variablen, aber nicht in konstanten Ausdrücken ersetzt werden kann.

Vergleichsoperatoren

Die neue Arbeitsweise des '~~' erklärt sich einfach mit dem Umstand, daß es ein
Vergleichoperator wie '==' ist. Der gibt ja auch nur wahr oder falsch zurück.
(im perlschen Sinne) Ohne zu übertreiben kann man sogar sagen, das '~~' der
Endgegner unter den Vergleichsoperatoren ist, noch weit mächtiger als bereits in
Perl 5.10. Je nach Typ und Inhalt seiner Operanden versucht er in jeder Lage
etwas sinnvolles abzuliefern und hat dazu eine große Tabelle, die Vorrang und
Kompatibilität aller internen Perlobjekte zueinander angibt. Das hat von Natur
aus immer etwas mit Unschärfe zu tun und tatsächlich sieht '~~' ein wenig aus,
wie das Symbol für das Wort circacirca.

Das genauer Gegenteil dazu ist der Operator '===', der die Identität, also Inhalt
und Typ zweier Werte vergleicht. Wem das immer noch nicht genau genug ist, kann
noch auf 'eqv' zurückgreifen, welches zusätzlich Laufzeiteigenschaften beachtet.

Perl 6:
1,2 === 1,2; # 0
'3.0' === 3; # 0

Das erste Beispiel ergab negativ, weil es Referenzen (heißen jetzt Captures) auf
2 unterschiedliche Arrays sind. Das zweite ist ebenfalls negativ, weil für Perl
offensichtlich ist, daß der linke Operand ein String ist und der rechte numerisch.

Gewöhnlich entgehen jedoch Perl-Programmierer solchen Problemen, in dem sie auch
beim Vergleich den bevorzugten Kontext erzwingen. Wie in Perl 5 zeigen Ops aus
mathematischen Symbolen ('<','>','<=','>=','==') den numerischen Kontext an.
Vergleichsops aus Buchstaben ('lt','gt','le','ge','eq') vergleichen zeichenweise.

Perl 6:
1,2 == 1,2; # 1
'3.d' == 3; # 1
1,2 eq 1,2; # 1
3.0 eq 3; # 1

Mit anderen Worten: '$a eq $b' ist eine Kurzschreibweise für '~$a === ~$b' und
'$a == $b' kann auch '+$a === +$b' formuliert werden. Was aber tun, wenn nicht
bekannt ist, ob man Zeichen oder Zahlen vergleichen wird? Dann nimmt man '~~',
was aber nur gut gehen kann, wenn beide Vergleichswerte immer noch den gleichen
Typ haben.

Perl 6:
'5' ~~ 5.0; # 1
'du' ~~ 'du'; # 1
3.0 ~~ '3f'; # 0

Die oberhalb aufgezählten Vergleichsops brauchen wohl kaum eine Vorstellung,
auch nicht ihre negierte Form (mit einem '!' davor). Selbst die verkürzten Formen
der 'ungleich'-Operatoren (numerisch '!=' für '!==') und (im string Kontext 'ne'
für '!eq'), haben bereits vor langer Zeit ihren Platz in der Perl-Folklore
gefunden (jeweils beide Schreibweisen sind in Perl 6 erlaubt). Und doch steckt
auch hier eine neue verallgemeinerte Logik hinter diesen Operatoren, denn es gibt
weit mehr sortierbare Dinge als Zahlen und Buchstaben, die man praktischerweise
mit einem Operator befragen kann, ob sie sich in der gewünschten Reihenfolge
befinden. Die Ops dafür sind 'before' und 'after', die leider derzeit noch nicht
von Pugs verstanden werden.

Perl 6:
say 3 before 5; # 1
say 'b' after 'a'; # 1

Somit ist klar, daß '$a > $b' eine andere Schreibart für '+$a after +$b' ist und
'$a lt $b' auch als '~$a before ~$b' ausgedrückt werden kann. All die bisherigen
Vergleichsop haben jedoch gemeinsam, daß sie ein wahr oder falsch liefern. Nun,
eigentlich ist es ein 'Bool::True' oder 'Bool::False', die im normalen, numerischen
Kontext zu 1 oder 0 evaluiert werden, doch heben wir uns diese Spitzfindigkeiten
für später auf, wenn das interne Objektsystem ansteht. Selbst wenn ich mehrere
Vergleiche kombiniere, wie in:

Perl 6:
say 3 < $b < 7; # wird in 1 Zug ausgewertet
say 3 < $b == $b < 7;# in 2 Zügen ausgewertet

erhalte ich einen boolschen Wert, da nur 2 verschiedene Ergebnisse möglich sind.
Im allgemeinen Vergleich sind es jedoch 3: kleiner als, gleich und größer als.
Was sich in den Rückgabewerten, wie in P5, als 1, 0 und 1 ausdrückt. Aber vor
der Umwandlung in den Ergebniskontext ist es ein 'Order::Increase', 'Order::Same'
oder 'Order::Decrease'. Diese und weitere internen Methoden sind auch dafür sehr
nützlich, daß ein Modul wie DateTime sie mit eigenen Routinen überladen kann.
(So etwas wie "Tie" gibt es hier nicht mehr.) Stellt dann der Befehl 'cmp' fest,
daß er hier 2 Objekte vom Typ DateTime vergleichen soll und die Parameterlisten
der bereitgestellten Methoden auch zu den Operanden passen, werden sie verwendet.
Und voila, 'cmp' kann auch Zeitpunkte vergleichen. Ähnlich arbeiten auch '++' und
'-
', die ebenfalls dank mitgelieferter Methoden, Vorgänger- und Nachfolger eines
Objektinhaltes ermitteln können. Jetzt hat mancher Perlkundiger sicher gerufen:
"Aber 'cmp' forciert doch den Stringkontext?". Richtig, in Perl 5, aber nicht in
Perl 6. Hier steht 'cmp' wirklich für 'compare' (vergleiche). Soll beim Vergleich
auf numerischen Kontext geachtet werden, so benutze man das bekannte '<=>', im
Strinkontext allerdings 'leg'. Das hört sich ausgesprochen 'LowerEqualGreater'
an und ist parallel zu <=>, an die Anfangsbuchstaben der Namen der verwandten
Ops 'lt', 'eq', 'gt' angelehnt.

Der dritte Skalarkontext

In diesem Text wurde es schon einige Male unterschwellig erwähnt, Perl 6 kennt
auch einen boolschen Kontext. Und er wird mit dem '?' gefordert, da einfache
Fragen auch meist mit ja oder nein beantwortbar sind. Wie der numerische, so
kennt der boolsche auch einen verneinenden Operator: das Ausrufungszeichen. Eine
Umwandlung in den boolschen Kontext tat man bisher eher implizit, z.B. beim
einsetzen einer Variable in eine if-Bedingung oder in Verbindung mit logischen
Operatoren. Jetzt geht es überall und explizit, auch mit 'true' oder 'not':

Perl 6:
say ? '2.3ah'; # 1
say true 0 ; # 0
say ! '2.3ah'; # ''
say not ''; # 1

Und sicher gibt es hier ebenfalls die kontexterzeugenden Bitoperatoren. Hier macht
es auch ein wenig mehr Sinn als bei Texten. Nur nicht für die Shiftoperatoren,
denn bei einem Bit Registerbreiter, gibt es nichts zu shiften.

Perl 6:
say 0 ?& 'aha'; # 0
say 3 ?| '2.3'; # 1
say '' ?^ 4 ; # 1

Steht das '?^' vor einem einzelnem Operanden, entspricht es einem '!' oder not.

Schlußendlich .. die Auswahloperatoren

Bei den logischen Operatoren gibt es noch eine wohlbekannte Gattung, die den
Kontext der Werte nicht verändert, auch wenn sie intern mit den Umwandlungen der
Werte in den boolschen Kontext rechnet. Ich nenne sie Auswahloperatoren, da sie
den Wert eines Operanden zurückliefern. Manche nennen sie Kurzschlußoperatoren,
da sie vorzeitig abbrechen, wenn das Ergebnis absehbar ist. Das kann man dazu
nutzen, Befehle bedingt ausführen zu lassen. Ich halte meine Bezeichnung für
treffender, da einer aus dieser Gattung (xor), diese Eigenschaft nicht besitzt.
Ihr Syntax wird durch die Wiederholung des logischen Operatorzeichens gebildet.

Perl 6:
say 0 && 'aha'; # 0
say 4 and 0 ; # 0
say 3 || 0 ; # 3
say 0 or 5 ; # 5
say '' ^^ 4 ; # 4
say 7 xor 4 ; # ''

Der mittlere Op hat eine recht neue Variante, die erst Perl 5.10 kennt. Sie nennt
sich defined-or und liefert nur dann den rechten Wert, wenn der linke undefniert
ist. Die 3 Operatoren des letzten Listings haben noch eine Schreibweise, in der
sie mit niedriger Priorität (in einem Ausdruck als Letzte) ausgeführt werden.
('and' statt '&&', or statt '||' und xor statt '^^'). Diese wird gebraucht um
Terme logisch zu verknüpfen, ohne allzuviel Klammern benutzen zu müssen, die die
Lesbarkeit erschweren würden.

Wirklich neu ist in dem ganzen nur das 'orelse'. Es funktioniert exakt wie ein
'or', nur das die Spezialvariable '$!' des linken Blocks, auch im rechten Block
weiterbesteht, falls dieser ausgeführt werden sollte. Dadurch wird dieser Befehl
nützlich, um in knapper Form einen Aufruf oder kleinen Block mit einem zweiten zu
verknüpfen, der im Falle eines Problems, die Fehlermeldung des Ersten auswerten
kann. Entsprechend überträgt andthen $_ in den zweiten Block und führt diesen aus,
wenn der erste erfolgreich war.

Perl 6:
do {...} orelse { say $! }
do {...} andthen { say $_ }

Der wiederum vertraute, ternäre Operator ist nicht direkt ein Auswahloperator,
funktioniert aber ähnlich und wurde auch optisch den Auswahloperatoren angepaßt.
Das war anders kaum möglich, da Fragezeichen, wie bekannt, jetzt den boolschen
Kontext erzwingen.

Perl 6:
$a = defined $b ?? $b !! $c;
$a = $b // $c; # kürzer

Die kaum noch bekannten Flipflopops wurden umbenannt, um Verwechslungen mit den
gleichnamigen Bereichsoperator zu vermeiden ('..' zu 'ff' und '...' zu 'fff').
Dateitestoperatoren wurden den Perl 6-Konventionen angepaßt und werden nun mit
':' statt '-' am Anfang geschrieben. Sie haben auch negierte Versionen (':!')
und können additiv gestapelt aber auch sonstig explizit als Junctions logisch
verknüpft werden.

Perl 6:
if $datei ~~ :e { say 'gibts' }
if $datei.:!e { say 'gabs' }
if $datei :r :w :x { ... }
if $datei :r | :x { ... }

Ich glaube das waren ausreichende Indizien für mein Argument, daß Perl 6 eine
operatorbasierende Sprache ist. Mehr wäre auf einmal auch schwer aufzunehmen.
Die bisher nicht Erwähnten Operatoren , die vor allem Arrays betreffen, wir der
nächste Teil beschreiben.


Previous Chapter | Overview | Next Chapter

permalink
Perl 6 Tutorial

This tutorial was written for $foo Perl magazine and was published from winter 2007 until autumn 2009, later also in the wiki of the german Perl community. Much editing is being done afterwards to keep the content valid, because Perl 6 is still moving. To understand this tutorial you need basic Perl and general programming skills. Most terms are explained on the way. If you would like to deepen your knowledge, read the Perl 6 Tablets.

Part 1 : Basic Syntax

Part 2 : Operators for Scalars

Part 3 : Operators for Arrays

Part 4 : Hashes and control structures

Part 5 : Captures and Subroutines

Part 6 : Objects and Roles

Part 7 : Text, Rules and Grammars

Part 8 : Introspection and Metaprogramming

permalink
Weblog Navigation
Loading...
Weblog Archives
  • Loading...

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: