Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Unterprogramme

Unterprogramme sind wiederverwendbare Codebloecke in Perl. PetaPerl implementiert Perl5-kompatible Unterprogramme mit vollstaendiger Unterstuetzung fuer lexikalische Variablen, Closures und Signaturen.

Deklaration

sub greet {
    print "Hello, World!\n";
}

# Benanntes Unterprogramm
sub add {
    my ($a, $b) = @_;
    return $a + $b;
}

# Anonymes Unterprogramm
my $sub = sub {
    my $x = shift;
    return $x * 2;
};

Argumente (@_)

Argumente werden ueber das spezielle @_-Array uebergeben. Jedes Element ist ein Alias auf die Variable des Aufrufers.

sub modify {
    $_[0] = "modified";  # Aendert die Variable des Aufrufers
}

my $x = "original";
modify($x);
say $x;  # "modified"

Argumente kopieren:

sub safe_modify {
    my ($val) = @_;      # Kopie, kein Alias
    $val = "modified";   # Beeinflusst den Aufrufer nicht
}

Direkter Zugriff:

sub first { $_[0] }
sub second { $_[1] }

Rueckgabewerte

Explizite Rueckgabe

sub max {
    my ($a, $b) = @_;
    return $a if $a > $b;
    return $b;
}

Implizite Rueckgabe

Der Wert des letzten Ausdrucks wird automatisch zurueckgegeben.

sub square {
    my $x = shift;
    $x * $x  # Implizite Rueckgabe
}

Kontextabhaengige Rueckgabe

sub get_data {
    if (wantarray) {
        return (1, 2, 3);      # Listenkontext
    } else {
        return "scalar data";  # Skalarer Kontext
    }
}

my @list = get_data();   # (1, 2, 3)
my $scalar = get_data(); # "scalar data"

Hinweis: PetaPerls wantarray funktioniert fuer direkte Aufrufe und die meisten gaengigen Muster. Bei komplexer Verschachtelung kann es Randfaelle geben.

Prototypen

Prototypen bieten Argumentpruefung zur Uebersetzungszeit und Kontexterzwingung.

sub scalar_arg ($) {
    my $x = shift;
}

sub array_arg (@) {
    my @vals = @_;
}

sub no_args () {
    return 42;
}

Gaengige Prototypen:

PrototypBedeutung
$Einzelnes Skalarargument
@Array von Argumenten (schluerfen)
%Hash (Liste gerader Laenge)
&Unterprogramm-Referenz
*Bareword oder Typeglob
\$Skalar-Referenz
\@Array-Referenz
\%Hash-Referenz
()Keine Argumente (konstantes Unterprogramm)

PetaPerl-Status: Grundlegendes Prototyp-Parsing implementiert. Laufzeitdurchsetzung teilweise.

Signaturen (experimentell)

Perl-5.20+-Signaturen bieten benannte Parameter mit optionalen Typbeschraenkungen und Standardwerten.

sub greet ($name, $greeting = "Hello") {
    say "$greeting, $name!";
}

greet("Alice");              # Hello, Alice!
greet("Bob", "Hi");          # Hi, Bob!

Schluerfen-Parameter:

sub sum ($first, @rest) {
    my $total = $first;
    $total += $_ for @rest;
    return $total;
}

sum(1, 2, 3, 4);  # 10

PetaPerl-Status: Signatur-Parsing im AST implementiert. Codegen senkt zu Pad-Allokation ab. Laufzeit-Parameterzuweisung funktioniert. Standardwerte und Schluerfen-Parameter funktionsfaehig.

Closures

Anonyme Unterprogramme erfassen lexikalische Variablen aus dem umschliessenden Gueltigkeitsbereich.

sub make_counter {
    my $count = 0;
    return sub {
        return ++$count;
    };
}

my $c1 = make_counter();
say $c1->();  # 1
say $c1->();  # 2

my $c2 = make_counter();
say $c2->();  # 1  (unabhaengiger Zaehler)

Closure-Erfassungsmechanismus:

  • Parser identifiziert aeussere lexikalische Variablen, die im Unterprogrammkoerper referenziert werden
  • Codegen zeichnet outer_refs-Zuordnung auf (sub_pad_slot, outer_pad_slot)
  • pp_anoncode erfasst Zellen zum Instanziierungszeitpunkt
  • pp_entersub teilt erfasste Zellen in den Pad des Unterprogramms

Beispiel: Closure-Fabrik

sub make_adder {
    my $offset = shift;
    return sub {
        my $x = shift;
        return $x + $offset;  # Erfasst $offset
    };
}

my $add5 = make_adder(5);
my $add10 = make_adder(10);

say $add5->(3);   # 8
say $add10->(3);  # 13

Jede Closure erfasst ihre eigene $offset-Zelle. Aenderungen betreffen nur die Instanz dieser Closure.

Methodenaufrufe

my $obj = Some::Class->new();
$obj->method($arg);

# Indirekte Objektnotation (nicht empfohlen)
method $obj $arg;

PetaPerl-Status: Methodendispatch ueber pp_method, pp_method_named. ISA-Suche implementiert. AUTOLOAD unterstuetzt.

Spezielle Unterprogramme

AUTOLOAD

Wird aufgerufen, wenn ein undefiniertes Unterprogramm aufgerufen wird.

package Foo;

our $AUTOLOAD;

sub AUTOLOAD {
    my $method = $AUTOLOAD;
    $method =~ s/.*:://;  # Paket entfernen
    print "Undefinierte Methode aufgerufen: $method\n";
}

Foo->undefined_method();  # Loest AUTOLOAD aus

PetaPerl-Status: AUTOLOAD-Dispatch in pp_entersub implementiert. Setzt $Package::AUTOLOAD korrekt.

BEGIN, END, INIT, CHECK, UNITCHECK

PetaPerl-Status: BEGIN-Bloecke werden waehrend der Kompilierung ausgefuehrt. END-Bloecke werden beim Programmende ausgefuehrt. INIT, CHECK und UNITCHECK haben teilweise Unterstuetzung.

Rekursion

Rekursive Aufrufe werden mit Tiefenlimitierung unterstuetzt.

sub factorial {
    my $n = shift;
    return 1 if $n <= 1;
    return $n * factorial($n - 1);
}

Rekursionslimit: Standardmaessig 1000 Ebenen (konfigurierbar ueber PPERL_MAX_RECURSION). Verhindert Stapelueberlauf.

Aufrufkonventionen

Mit Klammern

foo();        # Keine Argumente
foo($a);      # Ein Argument
foo($a, $b);  # Mehrere Argumente

Ohne Klammern (Kaufmanns-Und-Form)

&foo;   # Aktuelles @_ an foo uebergeben

Bei Aufruf als &foo (ohne Klammern) wird das aktuelle @_ an das aufgerufene Unterprogramm uebergeben. Dies ermoeglicht Wrapper-Funktionen:

sub wrapper {
    log_call();
    goto &real_function;  # Tail-Call mit aktuellem @_
}

Goto und Tail-Calls

sub outer {
    # ... Vorbereitung ...
    goto &inner;  # Aktuellen Aufrufrahmen ersetzen
}

goto &sub fuehrt eine Tail-Call-Optimierung durch: ersetzt den aktuellen Rahmen, anstatt einen neuen hinzuzufuegen.

PetaPerl-Status: goto &sub in pp_goto implementiert. Tail-Rekursions-Optimierung funktionsfaehig.

Erweiterte Funktionalitaeten

Konstante Unterprogramme

use constant PI => 3.14159;
use constant DEBUG => 0;

# Aequivalent zu:
sub PI () { 3.14159 }

Konstante Unterprogramme haben einen leeren Prototyp () und geben einen festen Wert zurueck. Perl kann sie zur Uebersetzungszeit einbetten.

PetaPerl-Status: use constant erzeugt konstante Unterprogramme. pp_entersub gibt den konstanten Wert direkt zurueck, ohne das Unterprogramm zu betreten.

Lvalue-Unterprogramme

sub get_value : lvalue {
    $global_var;
}

get_value() = 42;  # Weist $global_var zu

PetaPerl-Status: :lvalue-Attribut nicht implementiert. pp_leavesublv-Stub existiert.

State-Variablen

sub counter {
    state $count = 0;
    return ++$count;
}

state-Variablen bleiben ueber Aufrufe hinweg erhalten (werden einmal initialisiert).

PetaPerl-Status: State-Variablen sind implementiert. Variablen werden einmal initialisiert und bleiben ueber Aufrufe hinweg erhalten.

Eingebaute Funktionen

wantarray

Gibt den Aufrufkontext zurueck: undef (void), 0 (skalar), 1 (Liste).

sub context_aware {
    if (!defined wantarray) {
        say "Void-Kontext";
    } elsif (wantarray) {
        say "Listenkontext";
        return (1, 2, 3);
    } else {
        say "Skalarer Kontext";
        return 42;
    }
}

PetaPerl-Status: Teilweise Implementierung. Funktioniert fuer direkte Aufrufe. Kann bei komplexer Verschachtelung fehlschlagen.

caller

Gibt Informationen ueber den aufrufenden Stapelrahmen zurueck.

# Skalarer Kontext: Boolesch (gibt es einen Aufrufer?)
if (caller) {
    # Von einem anderen Unterprogramm aufgerufen
}

# Listenkontext: Detaillierte Informationen
my ($package, $filename, $line) = caller(0);
my ($pkg, $file, $line, $sub) = caller(1);  # Einen Rahmen hoeher

PetaPerl-Status: In pp_caller implementiert. Gibt (package, filename, line, subroutine, ...) im Listenkontext zurueck.

PetaPerl-Implementierungshinweise

Op-Tree-Struktur

Benannte Unterprogramme registrieren CV (Code Value) in der Arena:

SubDef -> lower_sub() -> NextState -> Body-Ops -> LeaveSub
                      |
                  Register CV(name, start_op, pad_size, outer_refs)

Anonyme Unterprogramme geben eine AnonCode-Operation aus:

AnonSub -> lower_anon_sub() -> NextState -> Body-Ops -> LeaveSub
                             |
                         AnonCode (legt CV auf den Stapel)

Aufrufsequenz

  1. Aufrufer legt Argumente auf den Stapel
  2. Aufrufer legt Markierung ab
  3. Aufrufer legt CV-Referenz ab
  4. EnterSub entnimmt CV, entnimmt Markierung, sammelt Argumente in aliasiertem @_
  5. Neuer Pad wird allokiert (Slot 0 = @_)
  6. Closure-Zellen werden in den Pad geteilt (falls vorhanden)
  7. Sprung zur start_op des CV
  8. Koerper wird ausgefuehrt
  9. LeaveSub sammelt Rueckgabewerte, stellt Pad wieder her
  10. Rueckkehr zum Aufrufer

Pad-Layout

Slot 0: @_ (immer, befuellt durch EnterSub)
Slot 1+: Lexikalische Variablen in Deklarationsreihenfolge

Signaturparameter werden zu Pad-Slots:

sub foo ($x, $y, @rest) { }

# Pad-Layout:
# 0: @_
# 1: $x
# 2: $y
# 3: @rest

Closure-Erfassung

Zweistufiger Prozess:

  1. Codegen: Aeussere lexikalische Variablen analysieren, outer_refs im CV aufzeichnen
  2. Laufzeit: pp_anoncode erfasst Zellen, pp_entersub teilt sie

Warum zwei Stufen? Closure-Fabriken benoetigen Erfassung pro Instanz, nicht pro Definition.

Bekannte Einschraenkungen

FunktionalitaetStatus
Benannte ParameterFunktioniert
PrototypenGeparst, teilweise Durchsetzung
SignaturenFunktioniert (grundlegend)
ClosuresFunktioniert
RekursionFunktioniert (1000 Ebenenlimit)
AUTOLOADFunktioniert
MethodendispatchFunktioniert
wantarrayTeilweise
callerFunktioniert
BEGIN/END-BloeckeBEGIN funktioniert; END/INIT/CHECK teilweise
Attribute (:lvalue usw.)Geparst, nicht durchgesetzt
goto &subFunktioniert
State-VariablenGrundlegende Unterstuetzung

Leistung

PetaPerl-Unterprogramme werden ueber den Interpreter mit Schnellpfad-Dispatch ausgefuehrt. Der JIT-Compiler (Cranelift) behandelt heisse Schleifen innerhalb von Unterprogrammen, kompiliert aber nicht den Unterprogrammaufruf/-ruecksprung selbst per JIT.

Die Funktionszeiger-Dispatchtabelle des Interpreters und eingebettete Schnellpfade fuer gaengige Operationen (Arithmetik, Vergleich, Zuweisung) minimieren den Aufwand pro Operation.

Closure-Aufwand: Zellenallokation pro erfasster Variable. Minimal zur Laufzeit (Arc-Klon).

Testen

Siehe t/05-op/sub.t, t/05-op/closure.t fuer umfassende Testabdeckung.