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:
| Prototyp | Bedeutung |
|---|---|
$ | 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_anoncodeerfasst Zellen zum Instanziierungszeitpunktpp_entersubteilt 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
- Aufrufer legt Argumente auf den Stapel
- Aufrufer legt Markierung ab
- Aufrufer legt CV-Referenz ab
EnterSubentnimmt CV, entnimmt Markierung, sammelt Argumente in aliasiertem@_- Neuer Pad wird allokiert (Slot 0 =
@_) - Closure-Zellen werden in den Pad geteilt (falls vorhanden)
- Sprung zur start_op des CV
- Koerper wird ausgefuehrt
LeaveSubsammelt Rueckgabewerte, stellt Pad wieder her- 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:
- Codegen: Aeussere lexikalische Variablen analysieren,
outer_refsim CV aufzeichnen - Laufzeit:
pp_anoncodeerfasst Zellen,pp_entersubteilt sie
Warum zwei Stufen? Closure-Fabriken benoetigen Erfassung pro Instanz, nicht pro Definition.
Bekannte Einschraenkungen
| Funktionalitaet | Status |
|---|---|
| Benannte Parameter | Funktioniert |
| Prototypen | Geparst, teilweise Durchsetzung |
| Signaturen | Funktioniert (grundlegend) |
| Closures | Funktioniert |
| Rekursion | Funktioniert (1000 Ebenenlimit) |
| AUTOLOAD | Funktioniert |
| Methodendispatch | Funktioniert |
| wantarray | Teilweise |
| caller | Funktioniert |
| BEGIN/END-Bloecke | BEGIN funktioniert; END/INIT/CHECK teilweise |
Attribute (:lvalue usw.) | Geparst, nicht durchgesetzt |
| goto &sub | Funktioniert |
| State-Variablen | Grundlegende 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.