Alles neu (Linux-Magazin, September 2016)

Perl6 beschert der Perl5-Syntax einen neuen Anstrich und führt ein richtiges Objektsystem ein. Ein Praxistest anhand der Reimplemtierung eines alten Blackjack-Spiels.

Totgesagte leben länger, heißt es, und das jahrzehntelang herbeigelechzte Perl6 scheint nun tatsächlich Gestalt anzunehmen. Das erfuhr ich kürzlich in Orlando im Bundesstaat Florida, als ich Gelegenheit hatte, eine Woche nach der Massenschießerei dort der Grassroots-Perl-Konferenz YAPC 2016 beizuwohnen.

Etwa zwei Drittel der Vorträge widmete sich Perl5-Themen, aber zu meinem bassen Erstaunen wenden sich nun mehr und mehr Leute in der Community allen Ernstes dem Nachfolger und jahrelangem Sorgenkind Perl6 zu. Schon vor sage und schreibe elf Jahren, auf der OSCON-Konferenz 2005 hatte Damian Conway vollmundig angekündigt, das "Endgame" der Entwicklung sei angebrochen, aber was dann folgte, waren mehrere Rückschläge und Neuanfänge, bis Ende letzten Jahres dann endlich die virtuelle Maschine Rakudo betriebsfähig war, die zwar nicht mit Performance punktet, aber doch zuverlässig Perl6-Code durchrattert.

Was ist Perl6?

Was ist Perl6 eigentlich? Um das zu erforschen, ist es hilfreich, zu analysieren, was Perl5 denn gefehlt hat. Manko Nummer eins ist ohne Zweifel ein fest eingebautes Objektsystem. In Perl5 war es nämlich nur notdürftig aufgenietet, und niemand, der einigermaßen bei Verstand war, hätte auch nur in einem mittelgroßen Programm die zu Klassen gestempelten Hashstrukturen oder als Hashelemente eingebaute Attribute verwendet.

Vielmehr haben professionelle Perl-Entwickler seit nunmehr bestimmt zehn Jahren das CPAN-Modul Moose oder einen seiner Ableger genutzt, um Klassen zu definieren und auf deren Attribute so zuzugreifen. Das befreit Entwickler nicht nur von der Fronarbeit, lästigen Boilerplate-Code einzutippen, sondern spannt auch ein Sicherheitsnetz auf, denn der Compiler merkt sofort, falls sich ein Tippfehler eingeschlichen hat. Bei einem falschen Hashkey ist das nicht der Fall und fatale Fehler kommen erst zur Laufzeit und unter Umständen langer Betriebszeit aus ihren Löchern. Eigentlich eine Selbstverständlichkeit für jede moderne Sprache, aber eben in Perl5 historisch bedingt leider nur als Flickwerk implementiert.

Geräuschvoller Code

Die Macher Larry Wall und Damian Conway spendierten Perl6 also Syntax zur Definition von Klassen und zum Aufrufen von Objektmethoden, denen Entwickler Listen mit benannten Parametern übergeben können. Nebenbei wurden auch noch einige logische Ungereimtheiten in der Perl5-Syntax bereinigt, die sich als Fallstricke für Neuanfänger erwiesen hatten. Und weil man schon mal dabei war, kam noch hier und da geklaute Syntax aus allen möglichen moderneren Sprachen wie Python oder Ruby hinzu, die irgendwie praktischen Nutzen versprach und clevere Tricks beim Programmieren verhieß. Herausgekommen ist eine Sprache mit relativ vielen Sonderzeichen, die die Kompatibilität zum alten Perl5 komplett zerbrach und einen neuen Interpreter namens Rakudo braucht.

Schrödingers Katze

Abbildung 1: Foto: Wikipedia, Martin Bahmann (https://commons.wikimedia.org/wiki/File:Schr%C3%B6dinger_cat.png).

Eines der etwas clever-exotischen neuen Konstrukte in Perl6 sind die sogenannten Superpositionen aus der Feder von Damian Conway. Letzterer hatte die Idee schon vor 13 Jahren auf Konferenzen hausiert und später in einem CPAN-Modul namens Quantum::Superpositions Quanten-Variablen in die Sprache eingeführt. Wie die quantenmechanische Katze in Schrödingers Experiment [4], die gleichzeitig tot und lebendig ist, nehmen diese Variablen mehrere Werte gleichzeitig an. So kann ein Integer gleichzeitig 1 und 2 sein, und addiert man 5 dazu, ist das Ergebnis gleichzeitig 6 und 7. Damals fiel mir dazu als Beispiel das Casino-Spiel Blackjack ein, bei dem ein As entweder 1 oder 11 Augen zählt.

Listing 1: Card.pm

    01 use v6;
    02 use strict;
    03 use Terminal::ANSIColor;
    04 
    05 class Blackjack::Card {
    06   has $.rank;
    07   has $.suit;
    08   has $.val;
    09   has $.suit_color;
    10  
    11   method Str() {
    12     return colored(
    13       "{self.rank}", self.suit_color );
    14   }
    15 }

Blackjack in Perl6

Im Jahre 2003 schrieb ich deshalb eine Kolumne im Linux-Magazin, zusammen mit einem Perl5-Skript, das ein Blackjack-Spiel mit einem Dealer in Las Vegas simuliert. Also dachte ich mir diesen Monat, was läge näher, als das alte Blackjack-Programm von anno dunnemals in modernem Perl6 nachzuimplementieren? Listing 1 zeigt, wie Perl6 den Klassen-Code für eine Blackjack-Karte implementiert. Kartenwerte heißen auf Englisch "Rank", also hat die Klasse Blackjack::Card in Listing 1 ein Attribut namens "rank" mit dem Default-Typ eines Perl-Skalars, der einen String oder Integer beherbergen darf. Wer in Perl5 schon mit dem Moose-Modul gearbeitet hat, der braucht sich also mit Perl6 nicht groß umzustellen, die neue Syntax ist fast identisch.

Neben einer Farbe (suit) verfügt jede Spielkarte noch über einen Zählwert (val), und die Darstellungsfarbe auf dem Bildschirm suit_color. Die Definitionen in Listing 1 beginnen mit einem Dollarzeichen, das die zugehörigen Attribute als Perl-Skalare auszeichnet. Alternativ bietet Perl6 für Attribute auch Arrays (@) und Hashes (%), sowie beliebige Klassennamen für Objektattribute. Der nach den "Sigils" genannten Sonderzeichen folgende Punkt zeichnet eine Variable als Objektattribut aus. Perl6 definiert dazu gleich hinter den Kulissen praktische Methoden für den lesenden und schreibenden Zugriff (z.B. obj.rank( "A" )), sowie benamte Parameterlisten für den Konstruktor, der dann Aufrufe wie Blackjack::Card.new( rank => "A" ) versteht. Wichtig auch: Perl6 ersetzt den Pfeil -> durch einen Punkt, aus $o->foo() in Perl5 wird so $o.foo() in Perl6. Um innerhalb eines Objekts auf das Objekt selbst zuzugreifen, dient das Schlüsselwort self (ohne Sigil).

Strings in Perl6 können nicht nur automatisch interpolierte Variablen wie in "Hallo $gast" enthalten, sondern auch dynamisch ausgeführte Code-Konstrukte in geschweiften Klammern, was besonders mit der neuen Objektsyntax hilft, falls der Wert nicht in einer Variablen steckt, sondern aus einer Methode stammt: "Hallo {$o.gast()}" ersetzt die geschweiften Klammern und alles dazwischen mit dem Rückgabewert der Methode gast() auf ein in $o liegenden Objekt.

Grundkurs Blackjack

Abbildung 2: Der Blackjack-Dealer hat sich eine offene Karte und jedem der vier Spieler am Tisch jeweils zwei Karten zugeteilt (Bild: Wikipedia).

Wie geht Blackjack nochmal in einem Casino in Las Vegas? An einem halbrunden Kartentisch mit grünem Filzbelag steht ein Kartenausgeber, der Dealer, einer Reihe von Spielern gegenüber und zieht aus einem sogenannten Schuh einzeln Karten von einem 52-blättrigen Pokerblatt.

Um den Kartenzählern unter den Spielern im Casino die Arbeit zu erschweren, enthält ein Kartenschuh gut gemischt bis zu acht Kartenspiele mit 52 Karten. Zuerst gibt der Dealer jedem Spieler am Tisch zwei offene Karten, und anschließend gibt er sich selbst eine offene und eine verdeckte Karte. Dann sind die Spieler an der Reihe, sie können entweder weitere Karten vom Dealer verlangen ("Hit") oder dankend ablehnen ("Stand"). Ziel des Spiels ist es, mit allen gezogenen Karten möglichst nahe an die Gesamtpunktzahl 21 zu kommen, ohne diese zu überschreiten, denn sonst verliert der Spieler automatisch. Sind die Spieler befriedigt, fängt der Dealer an, seine verdeckte Karte offenzulegen und eventuell für sich selbst weitere Karten aus dem Schuh zu ziehen.

Soft und Hard 17

In den Casinos von Las Vegas spielt der Dealer wie ein Roboter nach einem festen System: Er zieht neue Karten, bis er mindestens 17 Punkte hat. Kommt zum Beispiel eine Zehn aus dem Schuber, und hinterher eine Sechs, zieht er nochmal, auch wenn's dann meistens rappelt und er mit einer 10-wertigen Karte über das Ziel von 21 hinausschießt und automatisch verliert, auch wenn sein Gegenspieler zum Beispiel bereits bei 13 aufgehört hat zu ziehen. Diese Regel ist bei allen Casinos identisch, mit einer kleinen Variation, der sogenannten "Soft 17" [3]. Diese Regel bestimmt, ob der Dealer auch bei einer 17, die sich aus mindestens einem As zusammensetzt nochmal ziehen muss. Zieht der Dealer zum Beispiel eine As und eine Sechs, hat er 17 und zieht bei einer "Hard 17"-Regel nicht mehr. Schreibt das Casino hingegen "Soft 17" vor, muss er nochmals ziehen, weil seine 17 (wenn das As nicht als 11 sondern als 1 zählt) auch als 1 + 6 = 7 interpretiert werden kann. Das gibt dem Dealer übrigens statistisch einen kleinen Vorteil in die Hand, seine Gewinnchancen wachsen um 0.2%.

Abbildung 3: Das Blackjack-Spiel in Aktion: Hier gewinnt der Casino-Spieler gegen den Dealer.

Blackjack im Terminal

In der Kommandozeilenversion des nachfolgend vorgestellten Skripts blackjack.p6 folgt der Dealer "Soft 17"-Regel und tritt gegen einen einzigen Spieler, den Aufrufer des Skripts an. Abbildung 3 zeigt einen Durchgang, bei dem der Spieler gegen den Dealer gewinnt. Zuerst zieht der Dealer eine Dame ("Q" für Queen) und eine verdeckte Karte, die das Skript naturgemäß nicht anzeigt. Der Spieler erhält zwei Zweien und tippt "H" ("Hit"), um mehr Karten zu verlangen. Es kommt ein As ("A") nach, und nachdem ein As im Blackjack entweder eins oder elf zählt, ist der Wert der Kartenhand nun 5 oder 15, was das Skript mit "any(5,15)" anzeigt.

Ein erneuter Hit bringt eine Zehn, also zählt die Kartenhand entweder 15 oder 25, wobei letzteres unter den Tisch fällt, weil es 21 übersteigt. Wagemutig drückt der Spieler ein weiters Mal auf "H", erhält wie durch ein Wunder eine Sechs und hat nun genau 21, die alternative 31 fällt unter den Tisch. Der Spieler drückt "s" ("Stand") und dann ist der Dealer an der Reihe, dreht seine verdeckte Karte um (eine Vier), sodass er nun "Q-4" hält, also eine Dame und eine Vier, die zusammen 14 zählen. Das ist weniger als 17, also zieht er gemäß den Casino-Vorschriften eine weitere Karte, bekommt aber einen Buben ("J") und ist nun mit 27 weit über's Ziel hinausgeschossen, verliert also automatisch. Der Spieler erhält den Punkt ("Score: 1") und seine Gesamtpunktzahl ("Total") ist nun ebenfalls 1, da es sein erstes Spiel war. Stunden blendender Unterhaltung für die ganze Familie.

Karte im Pokerblatt

Alle Spielkarten in einem 52-Karten-Blatt repräsentiert ein Objekt der Klasse Blackjack::Deck in Listing 2 Damit der Perl-Parser gleich weiß, dass Perl6-Code vorliegt, und nicht total verwirrt an der neuen Syntax scheitert, gibt "use v6" am Dateianfang die Version in einer Syntax vor, die auch der Perl5-Interpreter versteht und mit einer klaren Fehlermeldung abbricht, sollte jemand ihn versehentlich darauf ansetzen.

Listing 2: Deck.pm

    01 use v6;
    02 use strict;
    03 use Blackjack::Card;
    04 
    05 class Blackjack::Deck {
    06   has @.ranks = 
    07     [ flat < A J Q K >, 2..10 ];
    08   has @.suits = 
    09     < Hearts Spades Diamonds Clubs >;
    10   has %.suit_color =
    11     flat self.suits Z 
    12     flat < red black magenta blue >;
    13   has @.cards;
    14 
    15   method shuffle() {
    16     for self.ranks -> $rank {
    17 
    18       my $val = 10;
    19       if $rank ~~ /\d+/ {
    20         $val = $rank.Int();
    21       } elsif $rank eq "A" {
    22         $val = 1|11;
    23       }
    24 
    25       for self.suits -> $suit {
    26         self.cards.push(
    27           Blackjack::Card.new(:$rank, 
    28               :$suit, :$val, 
    29               suit_color => 
    30               self.suit_color{ $suit } ));
    31       }
    32     }
    33     self.cards = self.cards.pick( * );
    34   }
    35 
    36   method pick() {
    37     if self.cards.elems == 0 {
    38        self.shuffle;
    39     }
    40     return self.cards.shift;
    41   }
    42 }

Zeile 7 definiert mit dem Ausdruck

    flat < A J Q K >, 2..10

eine Liste mit allen möglichen Kartenwerten, As-Zehn-Bube-Dame-König, sowie die Zahlenwerte von 2 bis einschließlich 10. Durch Leerzeichen getrennte Werte in eckigen Klammern ist die Perl6-Syntax für die qw(...)-Listen in Perl5. Den Range-Operator ".." gab's in Perl5 auch schon, allerdings münden solche Konstrukte nicht wie in Perl5 in einer langen Liste von Werten, sondern in Listen von Listen, sodass der Ausdruck den zusätzlichen Operator flat braucht, um das Ganze zu einer langen Liste auszuflachen.

Um nun alle Karten im Spiel zu simulieren, geht die Methode shuffle() ab Zeile 15 in Listing 2 mittels zwei For-Schleifen durch die Attribute @.ranks und @.suits, kombiniert jeweils zwei Einträge zu einer Karte der Klasse Blackjack::Card, und schiebt das so neu gewonnene Objekt auf den Array @.cards. Die Zeilen 18 bis 23 bestimmen den Zählwert jeder Karte, der Eintrag 1|11 für ein As ist eine der vorher besprochenen Superpositionen aus den Werten 1 und 11.

Reißverschlussverfahren

Welche Bildschirmfarbe der Hash %.suit_color den Kartenfarben zuordnet, bestimmt sich aus der Verknüpfung von @.suits und einer ausgeflachten Liste von Bildschirmfarben in Zeile 12. Der Operator "Z" in Zeile 11 wendet auf die beiden Listen das Reißverschlussverfahren an ("Z" kommt vom englischen Wort für Reißverschluss, "zipper") und weist so jeder Kartenfarbe im Hash eine Bildschirmfarbe zu. Die For-Schleifen in Perl6 unterscheiden sich syntaktisch von Schleifen in Perl5, auf das Schlüsselwort for folgt zunächst eine Liste oder ein Array, danach ein Pfeil-Operator und dann der Name der Variable, die den aktuellen Iterationswert annimmt.

Um das Kartenspiel zu mischen, wendet Zeile 33 einen cleveren Trick mit dem sogenannten Whatever-Operator * an: Die Methode pick auf einen Array wählt ein willkürliches Element aus, entfernt es aus dem Array und gibt es zurück. Der Whatever-Operator weist Perl6 an, immer weiter zu machen, bis der Array leer ist und mischt so den ursprünglichen Array durch, gibt ihn als Liste zurück, und die Variable auf der linken Seite der Gleichung erhält die Werte zugewiesen. Die ab Zeile 36 definierte Methode gleichen Namens entfernt hingegen eine Karte aus dem gemischten Kartenstapel und gibt sie dem Aufrufer zurück.

Punkten mit Quanten

Alle Karten, die ein Spieler gerade hält, nennt man auf Englisch eine "hand", darum implementiert Listing 3 die Klasse Blackjack::Hand. Die Methode draw() ab Zeile 9 nimmt ein Objekt vom Typ Blackjack::Card entgegen und fügt es zum Blatt des Spielers hinzu. Um den Zählwert (oder besser gesagt die überlagerten Zählwerte) des Blatts zu ermitteln, addiert die Methode values() ab Zeile 20 die Zählwerte aller Karten auf. Der kritische Wert von 21 ist überschritten, falls keiner der überlagerten Zustände 21 oder weniger anzeigt, und die Methode is_busted() ab Zeile 28 gibt in diesem Fall einen wahren Wert zurück (von Englisch "busted" für "kaputt").

Listing 3: Hand.pm

    01 use v6;
    02 use strict;
    03 use Blackjack::Card;
    04 
    05 class Blackjack::Hand {
    06   has @.cards;
    07   has $.name;
    08 
    09   method draw( Blackjack::Card $card ) {
    10     push self.cards, $card;
    11   }
    12 
    13   method highval() {
    14     my $vals = 
    15       grep { $_ <= 21 }, self.values;
    16     return "BUSTED" if !$vals;
    17     return $vals;
    18   }
    19 
    20   method values() {
    21     my $total = 0;
    22     for self.cards -> $card {
    23       $total += $card.val;
    24     }
    25     return $total;
    26   }
    27 
    28   method is_busted() {
    29     return !( self.values <= 21 );
    30   }
    31 
    32   method Str() {
    33     my $str = "";
    34     for self.cards -> $card {
    35       $str ~= "-" if $str.chars;
    36       $str ~= "$card";
    37     }
    38     return "{self.name}: " ~
    39       "$str ({self.highval})";
    40   }
    41 
    42   method is_blackjack() {
    43     return self.cards.elems == 2 && 
    44     self.values == 21;
    45   }
    46 
    47   method score( Blackjack::Hand $other ) {
    48     return -1   if self.is_busted;
    49     return  1   if $other.is_busted;
    50     return  0   if self.is_blackjack and 
    51       $other.is_blackjack;
    52     return  1.5 if self.is_blackjack;
    53     return -1   if $other.is_blackjack;
    54     self.highval > $other.highval ??
    55         return 1 !!
    56         return 0;
    57   }
    58 }

Um das Blatt eines Spielers anzuzeigen, soll es genügen, ein Objekt der Klasse Blackjack::Hand in einen Stringkontext (also innerhalb doppelter Anführungszeichen) zu stellen. Das veranlasst Perl6 dazu, die Methode Str() des Objekts aufzurufen und deren Rückgabewert einzusetzen. Die Funktion ab Zeile 32 von Listing 3 iteriert dazu mit der Methode cards() durch alle Karten des Spielerblattes, stellt deren Objekte ihrerseits in Zeile 36 in einen String-Kontext, und verbindet die Ergebnisse durch Gedankenstriche. Der ab Zeile 38 zurückgereichte String enthält den Namen des Spielers, die Karten des Blattes, sowie die mittels highval() ermittelten Spielwerte. Der Operator zum Verknüpfen von zwei Strings hat sich von einem Punkt (.) in Perl5 zu einer Tilde (~) in Perl6 geändert. Folglich nutzt Zeile 39 ~=, um den rechts stehenden String an den links stehenden anzuhängen.

Jackpot mit Blackjack

Hält ein Spieler eine 10-wertige Karte zusammen mit einem As, zählt dies als sogenannter "Blackjack" und falls der Dealer nicht ebenfalls mit einer solchen Kombination aufwartet, erhält der Spieler das 1.5fache seines Einsatzes als Gewinn ausgezahlt. Die Methode is_blackjack() prüft diese Kombination ab Zeile 42, indem sie sicherstellt, dass das Blatt genau aus zwei Karten besteht und einer der überlagerten Zustände genau 21 Augen zählt. Die dem Spieler zustehenden Gewinnpunkte ermittelt später die Methode score() ab Zeile 47, die als Argument das Objekt des Dealerblattes zum Vergleich erwartet. Sie gibt -1 zurück, falls der Spieler einen "Bust" verursacht hat, egal welche Augenzahl der Dealer hat. Falls der Dealer "busted", kommt 1 zurück, also ist der Spieler einen Punkt im Plus. Falls beide einen Blackjack haben, ist das Ergebnis unentschieden, also kommt 0 zurück, und so weiter. Zu beachten ist, dass der sogenannte "ternary operator" in Perl5, der mit ...?...:..., abhängig von einem Test, entweder den ersten oder zweiten Ausdruck zurückgibt, in Perl6 zu ...??...!!... wird.

Listing 4: blackjack.p6

    01 #!/usr/bin/env perl6-in-docker.sh
    02 
    03 use v6;
    04 use lib '/perl6';
    05 use Blackjack::Card;
    06 use Blackjack::Hand;
    07 use Blackjack::Deck;
    08 
    09 my $TTY   = open("/dev/tty");
    10 my $deck  = Blackjack::Deck.new;
    11 my $total = 0;
    12 
    13 while ( 1 ) {
    14   my $dealer = 
    15    Blackjack::Hand.new( name => "Dealer" );
    16   my $player =
    17    Blackjack::Hand.new( name => "Player" );
    18   
    19   $player.draw( $deck.pick );
    20   $player.draw( $deck.pick );
    21   
    22   $dealer.draw( $deck.pick );
    23   say "$dealer";
    24   $dealer.draw( $deck.pick );
    25   
    26   while !$player.is_busted {
    27     say "$player";
    28     my $in = 
    29       prompt-char("Your move (h/s/q):");
    30     say "";
    31     given ( $in ) {
    32       when 'q' { exit( 0 ); }
    33       when 's' { last; }
    34       when 'h' { 
    35         $player.draw( $deck.pick ); }
    36     }
    37   }
    38   
    39   while !$player.is_busted and
    40         !$dealer.is_busted and
    41         $dealer.values < 17 {
    42     say "$dealer";
    43     $dealer.draw( $deck.pick );
    44   }
    45   
    46   say "$player";
    47   say "$dealer";
    48   
    49   my $score = $player.score( $dealer );
    50   $total += $score;
    51 
    52   say "Score: $score";
    53   say "Total: $total\n\n";
    54 }
    55 
    56 sub prompt-char($prompt) {
    57   ENTER shell "stty raw -echo min 1";
    58   LEAVE shell "stty sane";
    59  
    60   print $prompt;
    61   my $in = $TTY.read(1).decode;
    62   say "\r";
    63   return $in;
    64 }

Kommandozeilen-Casino

Das steuernde Perlskript des Kommandozeilen-Casinos blackjack.p6 steht in Listing 4 und enthält in seiner ersten Zeile einen Verweis auf den ausführenden Perl6-Interpreter. Doch wo Perl6 installieren? Auf perl6.org findet sich ein Tarball, den der abenteuerlustige Admin selbst kompilieren kann, während hunderte von Warnungen über den Bildschirm flitzen. Am einfachsten holt der moderne Linux-Enthusiast sich aber mittels

    docker pull rakudo-star

ein Docker-Image mit installiertem Perl6-Rakudo. Um nun ein auf dem Host liegendes Perl6-Skript im Perl6-Interpreter innerhalb des Docker-Containers aufzurufen, stellt Listing 4 in seine erste, sogenannte Shebang-Zeile, den Pfad zum Shell-Skript in Listing 5. Dieses startet den Docker-Container, definiert einen Mount auf das aktuelle Verzeichnis auf dem Host, das daraufhin innerhalb des Containers unter "/perl6" sichtbar ist. Listing 4 fügt deswegen in Zeile 4 einen zusätzlichen Suchpfad für Module als use lib '/perl6' ein, damit es ddie Blackjack-Module in den anderen Listings auch findet. Da der Linux-Kernel aus Sicherheitsgründen keine Shellskripts als Shebang-Programme zulässt, nutzt Listing 4 den Helfer /usr/bin/env als ausführendes Programm, das seinerseits das Shellskript aufruft, das akzeptiert der Linux-Kernel dann.

Listing 5: perl6-in-docker.sh

    1 #!/bin/sh
    2 
    3 sudo docker run -v `pwd`:/perl6 -i \
    4   -t ready /usr/bin/perl6 /perl6/$*

Spielmodus

Tastatureingaben von der Kommadozeile erfordern es normalerweise, dass der User jede Eingabe mit der Return-Taste abschließt, aber im Spielmodus soll das Blackjack-Skript direkt auf Tastendrücke reagieren. Dazu dient auf Unix-Systemen der Terminal-Modus "Raw", den man allerdings nur während der eigentlichen Eingabe aktivieren sollte, um ihn sofort danach in den normalen "cooked"-Modus zurückzusetzen, denn sonst reagiert das Terminal nicht mehr ordnungsgemäß auf später nach Programmschluss eingetippte Shell-Kommandos.

Deshalb setzt die ab Zeile 56 definierte Funktion prompt-char() (Perl6 erlaubt nun Gedankenstriche in Funktionsnamen) beim Eintritt ein Shell-Kommando mittels der Utility stty ab, um den Raw-Modus einzuschalten, um ihn beim Verlassen der Routine wieder zu deaktivieren. Perl6 bietet hierzu die Schlüsselwörter ENTER und LEAVE, um Aktionen beim Eintreten und später beim Verlassen einer Funktion auszuführen. Vom vorher geöffneten Terminal in der Variablen $TTY liest read(1) in prompt-char() ein Byte und die nachgeschaltete Methode decode() macht aus dem eingelesenen Buffer-Byte ein ASCII-Zeichen, das das Skript später mit einem Buchstaben wie "h" (hit) oder "s" (stand) vergleichen kann.

Perl6 verfügt über ein Switch-Statement, das "given/when" heißt und ab Zeile 31 zum Einsatz kommt, um die Tastatureingabe des Spielers mit vorgegebenen Buchstaben zu vergleichen und die entsprechende Spielsteuerung einzuleiten.

Perl6 versus Perl5

Der Spielfluss in Listing 4 ist relativ selbsterklärend, und wenn man die zur Implementierung notwendigen Codezeilen vergleicht, kommt Perl6 besser weg. Auch die fehlenden runden Klammern bei den Einleitungen zu for- und while-Schleifen wirken sich positiv auf die Lesbarkeit aus. Wird sich Perl6 durchsetzen? Es ist vielleicht zu früh um diese Frage zu beantworten, der Praxistest wird sie entscheiden. Auf der Konferenz in Orlando setzte Larry Wall das Gerücht in die Welt, an einem Perl6-Buch zu arbeiten, ließ aber offen, wie lange es zur Veröffentlichung hin ist oder ob das Projekt überhaupt irgendwann Früchte tragen wird. Falls man irgendwelche Lehren aus der Vergangenheit ziehen sollte, dann die, dass durchaus einige Zeit ins Land ziehen könnte, bis sich etwas rührt in Larry's privatem Imperium.

[1]

Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2016/09/Perl

[2]

"Quanten-Casino", Linux-Magazin 12/2003, http://perlmeister.com/snapshots/200312/index.html

[3]

"Blackjack Soft 17 Rule", http://www.readybetgo.com/blackjack/strategy/soft-17-rule-2496.html

[4]

Schrödingers Katze auf Wikipedia, https://de.wikipedia.org/wiki/Schr%C3%B6dingers_Katze

Michael Schilli

arbeitet als Software-Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.