Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Konvertieren, int ceil sprintf hex oct floor Runden AbschneidenPerl Kurs Skripting Funktionen Unterprogramme Subroutinen Parameter Argumente eval wantarray Memoizing Funktionen Unterprogramme Subroutinen Parameter Argumente eval wantarray Memoizing Perl Kurs Kapitel 10

10.4. Rückgabewert           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Damit eine Funktion einen Wert zurückliefern kann, benötigen Sie das Schlüsselwort return.  Hier ein Beispiel wie Sie einen Wert aus einer Funktion zurückgeben können ...

#!/usr/bin/perl -w

sub fakul
 {
  my $x=$y=$_[0];
  while(--$x)          #Solang $x-1 bis 0
    {
      $y*=$x;
    }
  return $y;  #Rückgabewert der Funktion fakul
}

print "Fakultätberechnung! Von welcher Zahl : ";
chomp ($var=<STDIN>);

$ergebnis=fakul($var);
#Funktion fakul gibt das Ergebnis mittels return zurück

print $ergebnis , "\n";

Die berühmte Berechnung der Fakultät, die in fast jeder Programmiersprache demonstriert wird. Hier sehen Sie also wie mittels return $y der Wert an den Funktionsaufruf ...

$ergebnis=fakul($var);

... an die Variable $ergebnis übergeben wird. Jetzt werden Sie sich sicherlich fragen, ob es in Perl auch möglich ist, wie in anderen Programmiersprachen, die Fakultät, Rekursiv zu berechnen. Hier das Beispiel Fakultät rekursiv ...

#!/usr/bin/perl -w

sub fakul
 {
  my $x=$_[0];
  if($x)    #Abbruchbedienung der Rekursion
    {
      return $x * fakul($x-1);   #Rekursiver Aufruf
    }
  return 1;
 }

print "Fakultätberechnung! Von welcher Zahl : ";
chomp ($var=<STDIN>);

$ergebnis=fakul($var);
#Funktion fakul gibt das Ergebnis mittels return zurück

print $ergebnis , "\n";

Ich möchte jetzt nicht so genau darauf eingehen. Doch für Anfänger sei kurz zur Rekursion gesagt: Eine Rekursion ist eine Funktion die sich immer wieder selbst aufruft bis eine bestimmte Bedingung erfüllt ist. Diese Bedingung stellt gleichzeitig die Abbruchbedienung der Rekursion dar.

Mit dem Schlüsselwort return wird auch eine Funktion beendet. Beispiel ...

#!/usr/bin/perl -w

sub func
 {
  print "Ich werde ausgegeben\n";
  return 1;
  print "Ich leider nicht mehr\n";
 }

func();

In diesem Beispiel wird nur die erste print-Anweisung ausgeführt. Die zweite nicht mehr, da die Funktion an den Aufrufer den Wert 1 zurückgibt und sich somit beendet. Nicht wegen des Wertes 1 sondern wegen dem Schlüsselwort return. Sie können diese Beispiel ja auch mal ohne den Wert 1 schreiben.

10.5. eval           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

In Perl ist es möglich, während der Laufzeit Programmcode dynamisch zu erzeugen und Auszuführen. eval() ist eine sehr mächtige Funktion. Ich werde daher nur auf die Grundlegenden Funktionen von eval() eingehen.

eval() können Sie auf zwei Verschiedenen Arten verwenden. Zum einen als Codeangabe in Strings ...

#!/usr/bin/perl -w

print "Bitte geben Sie einen Code ein : ";
chomp($code=<>);

eval $code; #Code wird ausgeführt

Sie können ja Beispielsweise folgendes eingeben ...

Bitte geben Sie einen Code ein : print $value=20*10 , "\n";

Die Ausgabe ist anschließend 200. Vorraussetzung ist natürlich, dass Sie auch ausführbaren Code eingegeben haben.

Von der Stringmethode, Code zu evaluieren, wird aber aus Sicherheitsgründen abgeraten. Da man nicht davon ausgehen kann, dass alle Benutzer einfache Anwender sind, kann ein Übelgesinnter schnell dazu Übergehen, gefährlichen Code einzugeben.

Die zweite Möglichkeit ist es, denn Code in geschweiften Klammern zu Schreiben ...

#!/usr/bin/perl -w

while(1){
  eval { $wert1=<>; $wert2=<>; $erg=$wert1/$wert2 };
  print "Ergebnis : $erg \n" unless $@;
  }

Der Vorteil an dieser Schreibweise ist, dass der Code, zur Kompilierzeit nur einmal geparst wird. Geben Sie in diesem Beispiel als Wert ein ungültiges Zeichen ein, wird anstatt einem Programmabbruch, ein Fehlercode ausgegeben. Dieser Fehlercode befindet sich in der reservierten Variablen $@. Tritt kein Fehler auf, ist diese Variable leer.

Eine Sinnvolle Anwendung der Funktion eval() wäre die Auswertung einer Ausnahmebehandlung mit try() und catch() wie dies in C++ oder Java verwendet wird ...

try{ /*Code der Ausnahme auslösen könnte*/ }
catch { /*Code der die Ausnahme behandelt*/ }

Und hier das ganze in der Praxis ...

#!/usr/bin/perl -w

$wert1=<>;
$wert2=<>;
chomp $wert1;
chomp $wert2;

eval { $ergebnis=$wert1/$wert2; }
or do{ print "FEHLER_HANDLER : $@\n" if $@;
        $ergebnis=0;
        };

print "$ergebnis \n";

Mit diesem Programm können Sie zum Beispiel verhindern, dass das Programm bei einer Division durch 0, nicht abrupt abgebrochen wird. In eval{...} evaluieren wir den Code. Falls eine reguläre Division erfolgt, wird dies als Ergebnis ausgegeben. Falls Sie aber zum Beispiel 5/0 rechnen wollen, geht es in den or do Anweisungsblock. In diesem wird die reservierte eval-Fehler-Variable ($@) überprüft, ob sich darin etwas befindet. Falls ja, wird der Fehlerhandler ausgeführt und die Variable $ergebnis auf 0 gesetzt.

Wie gesagt das Thema eval() ist sehr Umfangreich und es wurde nur kurz behandelt.

10.6. wantarray           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Wenn Sie eine kontexintesive Funktion schreiben wollen, ist auch dies in Perl kein Problem. Mit der Funktion wantarray() können Sie überprüfen, ob Ihre Funktion im Listenkontex aufgerufen wurde (true) oder im skalaren Kontex (false). Dabei gehen Sie wie folgt vor ...

sub func{
   if(wantarray){
     #Rückgabewert ist @array
     }
   else{
     #Rückgabewert $skalar
     }
}

Und so sieht eine Kontexintesive Funktion in der Praxis aus ...

#!/usr/bin/perl -w

sub kontex{
   if(wantarray){
     my @add = @_;
     my $tmp;

     foreach $add(@add){
       $add=$tmp+=$add;
       }
     return @add;
     }
   else{
     my($tmp, $add);
     foreach $add(@_){
       $tmp+=$add;
       }
     return $tmp;
   }
}

@liste = (1..10);

$tot = kontex(@liste);
print $tot , "\n";

@res = kontex(@liste);
print "@res\n"

10.7. Memoizing           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Memoizing ist eine Technik die viele aufwendige Funktionen in Perl beschleunigen kann. Die Prinzip funktioniert mit einer Cache in der bereits gerechnete Ergebnisse zwischengespeichert werden. Bei einer erneuten Berechnung mit der Funktion wird also erst mal in die Cache gesehen, ob dieser bereits berechnete Werte dazu beinhaltet. Falls ja, wird die Cache verwendet und es kann auf eine erneute aufwendige Berechnungen verzichtet werden. Befindet sich kein bereits berechneter Wert in der Cache, muss eben eine aufwendige Berechnung durchgeführt werden.

Am besten können Sie das ganze Anhand der Berechnung der Fibonaccizahlen sehen ...

#!/usr/bin/perl -w

{
 local %cache;
 sub fibonacci_cache{
   return 1 if $_[0] == 0 || $_[0] == 1;
   return $cache{$_[0]} if exists $cache{$_[0]};
   return $cache{$_[0]} = fibonacci_cache($_[0]-1) + fibonacci_cache($_[0]-2);
   }
}

sub fibonacci_nocache{
   return 1 if $_[0] == 0 || $_[0] == 1;
   return fibonacci_nocache($_[0]-1) + fibonacci_nocache($_[0]-2);
   }

print "Fibonacci mit Cache........\n";
$fib1 = fibonacci_cache(30);
print $fib1 , "\n";

print "Fibonacci ohne Cache.........\n";
$fib2 = fibonacci_nocache(30);
print $fib2 , "\n";

Die Berechnung mit Cache (fibonacci_cache) ist von der Zeit her nicht Erwähnenswert. Ohne Cache dagegen warten Sie hier schon ein paar Sekunden.

Die Unterbringung des Caches mussten Sie in einem Block mit einer dynamischen lokalen Variablen verwenden.

Obwohl hier ein  Hash verwendet wurde, muss dies nicht unbedingt so gemacht werden. Im Gegenteil, mit Arrays könnten Sie die Perfomance noch mehr Beschleunigen, da Array weniger Speicher benötigen.

Memoizing würde sich auch sehr gut für eine aufwendige Sortierung eignen.