Aus Linux-Magazin 01/2009

Stolperstellen für Perl-Eleven

© Bernd Lang, Pixelio.de

Gerade das leicht erlernbare Perl, mit dem der Autor seine Informatik-Vorlesungen immer beginnt, weckt bei Studierenden Kreativität und Motivation. Was allerdings nicht vor Missverständnissen schützt, die zuweilen Schmunzeln lassen. Einige Perlen der Programmierirrtümer mit Perl stellt dieser Beitrag vor.

Dieser Beitrag präsentiert ein paar bemerkenswerte und zuweilen amüsante Stolperstellen, Missverständnisse und besonders “kreative” Lösungsansätze von Studierenden für Perl-Aufgaben.

Ohne zu rechnen

Wenn ich in die Grundlagen von Perl einführe, erläutere ich unter anderem die Funktion »reverse()« ausführlich und anhand von Beispielen. Diese Funktion dreht im Listenkontext die Reihenfolge der Elemente eines Array um. Im Skalarkontext verkettet sie alle Zeichen und liefert sie einzeln in umgekehrter Reihenfolge zurück.

Aufbauend auf dieser Perl-Funktion sollten vor ein paar Jahren die Studenten als einfache Aufgabe ein Perl-Programm realisieren, das vier Textzeilen einliest und sie dann in umgekehrter Reihenfolge, außerdem mit jeweils umgedrehtem Textinhalt, wieder ausgibt.

Die vierzeilige Eingabe für dieses Übungsprogramm lautete:

Na klar,
Perl
ist
spitze!

Als Ausgabe hatte ich vorgesehen:

!eztips
tsi
lreP
,ralk aN

Natürlich ging ich davon aus, dass die Probanden die eben behandelte Funktion »reverse()« verwenden würden. Das führte dann zu einem Programm, wie in Listing 1. Stattdessen lieferten einige aber eine besonders findige Lösung, die nur aus einer einzigen Zeile bestand:

print "!eztipsntsinlrePn,ralk aN";

Im nächsten Beispiel sollte Perls Wiederholungsoperator »x« Zeichen vervielfachen. Dabei galt es, die folgende Darstellung zu verdoppeln:

****************
*     Perl     *
*     doppelt  *
*     sehen    *
****************

Mit einem »HERE«-Dokument und dem Wiederholungsoperator »x« ließe sich diese Aufgabe wie folgt lösen:

$Dokument = <<TOKEN
****************
*     Perl     *
*     doppelt  *
*     sehen    *
****************
TOKEN
x 2;
print "ntHERE-Dokument: n", $Dokument, "n";

Statt den vorhandenen »x«-Operator zu verwenden, realisierten aber auch hier einige Studenten die Aufgabe – recht mühsam – nur mit »print«-Anweisungen.

Listing 1: Textumkehr,
Beispiellösung

01 for ($i = 1; $i <= 4; $i++) {   # for-Schleife
02    print "$i.Text: ";
03    $Text = <STDIN>;          # Text-Eingabe
04    $Satz .= $Text;           # Verkettung der Zeichen
05 }
07 $Satz_gedreht = reverse $Satz;  # mit reverse drehen
08 print "$Satz_gedrehtn";        # Ausgabe

Klein und gemein

Nach meinem Motto “Perl – klein, aber gemein” verführten die folgenden Aufgaben Studenten dann zu falschen Ergebnissen, wenn sie Vorrangregeln, Aliases und Klammern in Perl nicht berücksichtigten. Bei den auf einige Zeilen beschränkten Perl-Programmen in Listing 2 war die Voraussage der richtigen Ergebnisse gefragt, die hier ausnahmsweise hinter dem Kommentarzeichen folgen.

Listing 2: Gedanklich zu
lösen

01 $a = 5;
02 $b = 7;
03 print $b + $a * ++$b;   # 48
04 #***********************************
05 @a = 1 .. 3;
06 for (@a) {print $_ *= 2 + $_;}
07 print "@a";             # 3 8 15
08 #***********************************
09 $a = 1;
10 $a % 2 ? $a += 10 : $a += 2;
11 print "$a";             # 13

Ohne Ende

Ein Übungsprogramm läuft auch schon mal ungeplant endlos. Das passiert besonders gern dann, wenn der angehende Programmierer den Umgang mit einer lokalen (privaten) Variablen in einer Funktion nicht beherrscht.

So erntete ich auf die Frage, wie man die Zahlen 1 bis 5 an eine Funktion, die Quadrate berechnet, übergeben könne, anfangs nur mitleidiges Lächeln. Also forderte ich die Studenten auf, per For-Schleife den Wert der Variablen »$zahl« bei 5 beginnend herunterzuzählen und an die Funktion »quadrat()« zu übergeben. In ihr war durch »$zahl*$zahl« das Quadrat zu berechnen und der Wert wieder der Variablen »$zahl« zuzuweisen und auszugeben. Ruck, zuck kamen Programme heraus wie eines Listing 3 zeigt.

Anschließend gab es allerdings lange Gesichter, denn das Programm läuft endlos! Grund: Ohne das Wörtchen »my« vor der Variablen »$zahl« wird keine lokale Variable deklariert, sondern die Werte sind global. Dadurch gerät das sprunghaft wachsende Ergebnis der Quadratur jeweils zum neuen Startwert der rückwärts zählenden Schleife, die sich mit jedem Durchlauf weiter von ihrem Ende entfernt, ohne es jemals zu erreichen.

Listing 3:
Quadrieren

01 sub quadrat {
02    $zahl = $zahl*$zahl;    # ohne »my« endlos
03    print "Ergebnis   $zahln";
04 }
05 for ( $zahl = 5; $zahl >= 1; $zahl--) {
06         quadrat;
07 }

In Schleifen

Unter dem Motto “Perl für Wiederholer” ließe sich die Verwendung einer Foreach- beziehungsweise While-Schleife bei der Array-Manipulation behandeln.

Für die Array-Manipulation stehen in Perl mächtige Funktionen zur Verfügung: »push()« und »pop()« bearbeiten das Ende eines Array, während die Funktionen »shift()« und »unshift()« umgekehrt den Anfang eines Array manipulieren. So hängt »push()« Werte an das Ende des Array an und die Funktion »pop()« entfernt sie vom Ende. Die »shift()«-Funktion stellt entsprechende Werte an den Anfang eines Array, während »unshift()« sie wieder vom Anfang entfernt.

Bei Verwendung einer Foreach-Schleife durchläuft eine skalare Variable ein Array. Der Durchlauf des Array geschieht je nach Array-Manipulation in aufsteigender beziehungsweise absteigender Indexreihenfolge. Eine While-Schleife läuft dagegen so lange, wie ihre Bedingung wahr ist.

Nachdem ich die Zusicherung von meinen Studierenden hatte, alles bestens verstanden zu haben, galt es, ein Array mit einer Foreach- beziehungsweise While-Schleife zu durchlaufen und mit den genannten Funktionen zu manipulieren. Listing 5 zeigt verschiedene Versionen, deren Ergebnisse vorherzusagen sind. Zum schnelleren Verstehen stehen im Artikel ausnahmsweise die korrekten Lösungen als Kommentar im Code.

Hier hatte ich wohl zu viel erwartet, da viele Studenten beide Schleifen bei ihren Wiederholungen als gleichwertig ansahen, obwohl sie ganz unterschiedliche Ergebnisse liefern. Das Kapitel “Schleifen” bedarf wohl genauerer Erläuterung!

Reihenfolgen und Referenzen

Bei der Behandlung der Referenzierung und Dereferenzierung von Perl-Funktionen haben einige Studierende nach der Devise “Perl zum Raten” agiert. Beim folgenden Programm rieten viele Studenten die Lösung – obwohl sie bei diesem Thema nicht mehr zu den Anfängern zählten. Dabei kamen Lösungsvorschläge heraus wie etwa “Perl ist alles” oder auch “Möglich ist alles”. Richtig ist stattdessen jedoch “in Perl ist alles möglich”. (Listing 4).

Die kontextsensitive Funktion »wantarray()«, die »true« liefert, wenn man sie im Listenkontext aufruft, und »false«, wenn sie im skalaren Kontext steht, hat manchen Studenten arg ins Schleudern gebracht.

Listing 4:
Funktionsaufrufe

01 $ref = &amp;Perl_1;
02 $ref->('in ')->('ist')->('alles');
03 sub Perl_1 {print "$_[0]"; return &amp;Perl_3;}
04 sub Perl_2 {print "$_[0] moeglich "; return;}
05 sub Perl_3 {print "Perl $_[0] "; return &amp;Perl_2;}

Listing 5: Schleifen

01 @neu = ();
02 @alt = qw(ABC die Cleo lief im Schnee);
03 foreach (@alt) {push @neu, pop @alt;}
04 print "@neun";        # Schnee im lief
05 #**********************************************
06 @neu = ();
07 @alt = qw(ABC die Cleo lief im Schnee);
08 while (@alt) {push @neu, pop @alt;}
09 print "@neun";        # Schnee im lief Cleo die ABC
10 #**********************************************
11 @neu = ();
12 @alt = qw(ABC die Cleo lief im Schnee);
13 foreach (@alt) {push @neu, shift @alt;}
14 print "@neun";        # ABC die Cleo
15 #**********************************************
16 @neu = ();
17 @alt = qw(ABC die Cleo lief im Schnee);
18 while (@alt) {push @neu, shift @alt;}
19 print "@neun";        # ABC die Cleo lief im Schnee

Schleudertrauma nach »wantarray«

Im folgenden Beispiel übersahen die Perl-Eleven, dass »wantarray()« im ersten Teil eine Liste, im zweiten Teil aber einen String mit einem regulären Ausdruck bearbeitet.

In der Liste »@ein« tauscht der Code die letzten in der Reihung gespeicherten Elemente, nämlich »Perl«, »ist« und »spitze« und gibt sie aus. Hier ist die bereits bekannte For-Schleife mit einer Array-Manipulation am Werk. Beim String hingegen werden nur die einzelnen Wortgruppen getauscht (Listing 6).

Listing 6:
»wantarray« und Regex

01 sub func (@ $) {
02    if (wantarray) {
03       while () {
04          $ein = <DATA>;
05              if ($ein ne "n") {
06                 @ein = ($ein, @ein);
07              }
08              else {last;}
09              }
10              for(@ein) {push @aus, pop @ein;}
11                 return @aus;
12         }
13    else {
14       chomp ($_ = <DATA>);
15   s/(w+).*?(w+).*?(w+).*?(w+)/$3 $2 $1 $4/;
16        return $_;
17    }
18 }
19 @wert = func;
20 print "tAusgabe _1:n", @wert;
21 print "nn";
22 wert = func;
23 print "tAusgabe_2:n", $wert;
24 __END__
25 
26 Perl
27 ist
28 spitze
29 und bietet
30 eine umfassende
31 Programmiererfahrung
32 
33 Cleo fliegt 2008 wieder nicht auf den Mond!

Die richtige Lösung lautet für Ausgabe 1 »Perl ist spitze« und für Ausgabe 2 »2008 fliegt Cleo wieder nicht auf den Mond!«. Die Studierenden lieferten dagegen zum Teil ganz andere Lösungen wie »spitze 2008 fliegt Cleo wieder« oder »Perl ist spitze und bietet 2008 Cleo den Mond!« und ähnlich falsche Konstrukte. Selbst eine Lösung wie »Hat die Katze Cleo einen Raumanzug?«, die ganz offensichtlich gar nicht zum obigen Programm passen kann, war dennoch unter den Vorschlägen.

Perl, gemixt

“Perl zum Mixen” war unter den Studierenden enorm beliebt. Dabei geht es um die Funktion »splice()«. Es steht ein Ausgangs-Array zur Verfügung, das »splice()« ändern soll. Einige Studierende nahmen hier die Aufgabe zu wörtlich. Bei meiner Frage, welches Ergebnis sie bei dieser Aufgabe erzielten, antworteten sie: “Einen Kater.” Wie man sieht, ist hier ein Ergebnis offenbar auch ganz ohne Perl-Kenntnisse möglich.

@mixdrink = qw(Kaffee Weinbrand Mixbecher);

Der Weinbrand soll durch einen Cognac ersetzt werden und die Funktion »splice()« weitere Zutaten hinzufügen:

splice (@mixdrink, 1, 1, qw(Milch Cognac Wasser Eigelb));

Mokkalikör ersetzt jetzt das Element Wasser:

splice (@mixdrink, -3, 1, Mokkalikoer);
@Perl_Cocktail = splice (@mixdrink, 0);
print "@Perl_Cocktailn";

Die Lösung dieser Aufgabe lautet korrekt: »Kaffee Milch Cognac Mokkalikoer Eigelb Mixbecher«.

Soweit einige Beispiele für Übungsaufgaben, die meine studentischen Perl-Fans, trotz mancher Klippen, mit großer Freude und mit viel Engagement bearbeitet haben. (jcb)

Der Autor

Prof. Dr. Jürgen Schröter lehrt an der Fachhochschule Landshut an der Fakultät für Elektrotechnik und Wirtschaftsingenieurwesen und leitet dort auch das Rechenzentrum sowie das Labor Digitaltechnik. Daneben ist er Autor dreier Bücher über Perl, das neueste davon ist die stark erweiterte Neuauflage von “Grundwissen Perl” aus dem vergangenen Jahr.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben