Schrift
[thread]8952[/thread]

Variablen in Sub's - strict meckert rum. (Seite 2)



<< |< 1 2 3 >| >> 21 Einträge, 3 Seiten
pq
 2007-04-27 14:02
#76293 #76293
User since
2003-08-04
12209 Artikel
Admin1
[Homepage]
user image
[quote=popcorn5,26.04.2007, 19:35]klar habe ich eine sub in einer sub.[/quote]
ach ja, das ist so klar, oder wie?
Quote
mein script müsste vereinfacht eigentlich so aussehen:

Code: (dl )
1
2
3
4
5
6
7
8
9
sub eins
{
  my $var=1;
  sub zwei
  {
      print $var."\n";
  }
  &sub zwei();
}

Danke, habe verstanden warum die warnung auftaucht. Da ich die Var nach der ersten sub nicht mehr brauche ist das ok, wenn ich die warnungen einfach abschalte, es funktioniert ja.

warnungen abschalten ist in den allermeisten faellen *nicht* die richtige lösung. die lösung wäre
Code: (dl )
1
2
3
4
my $zwei = sub {
 print $var, "\n";
};
$zwei->();

denn die subroutine namens "zwei" in deinem beispiel ist zwar in der
andren sub definiert, aber trotzdem von aussen aufrufbar und auch
nach der ersten subroutine noch gültig.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
sid burn
 2007-04-27 18:59
#76294 #76294
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=GoodFella,26.April.2007, 23:56][quote=Sucher,26.04.2007, 23:41]Hallo,

nur aus Interesse: Warum möchte man denn eine sub in einer sub definieren?

Grüße,[/quote]
Um den Sub-Namespace möglichst klein zu halten und logische Einheiten beeinander zu belassen?[/quote]
Was den für ein Sub-Namespace?

Jede Subroutine egal wo du sie Definierst ist automatisch am Package gebunden. Ob du die Sub nun innerhalb einer anderen Sub Definierst ist volkommen egal.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/perl
use strict;
use warnings;

sub welt {
return "welt ";
sub hallo {
return "hallo ";
}
}

print hallo, welt, "\n";


Das Beispiel gibt dann "Hallo, Welt" aus.

Um das Package also von Funktionen freizuhalten bleibt letztendlich nur Annonyme Subroutinen zurück die du in lexikalische Variablen speicherst.

Allerdiengs wird dann dein Namensraum für Variablen größer.
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
PerlProfi
 2007-04-27 19:26
#76295 #76295
User since
2006-11-29
340 Artikel
BenutzerIn
[default_avatar]
Geht das denn in Perl nicht noch irgendwie anders ?
Anonyme Subroutinen müssen ja auch noch jedesmal neu interpretiert werden...
ptk
 2007-04-27 21:45
#76296 #76296
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
[quote=PerlProfi,27.04.2007, 17:26]Geht das denn in Perl nicht noch irgendwie anders ?
Anonyme Subroutinen müssen ja auch noch jedesmal neu interpretiert werden...[/quote]
In Perl muss *alles* interepretiert werden. Ist ja ein Interpreter.
sid burn
 2007-04-27 23:15
#76297 #76297
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=ptk,27.April.2007, 19:45][quote=PerlProfi,27.04.2007, 17:26]Geht das denn in Perl nicht noch irgendwie anders ?
Anonyme Subroutinen müssen ja auch noch jedesmal neu interpretiert werden...[/quote]
In Perl muss *alles* interepretiert werden. Ist ja ein Interpreter.[/quote]
Hmm, also ich würde Perl eher als eine Virtuelle Maschiene betrachten.

Bei jeder Ausführung des Skriptes wird der Sourcecode erst geparsed in Bytecode umgewandelt, dann wird es einer Virtuellen Maschiene übergeben der den bytecode ausführt.

Wie ein handelsüblicher Interpreter sieht mir das nicht aus. Sieht so ziemlich gleich aus wie Java. Mit dem einzigen Unterschied das ich das Umwandeln nach Bytecode nicht Manuell mache, sondern jedesmal vor dem Ausführen automatisch passiert.

Ansonsten sollten Anonyme Subroutinen nicht ständig neu Kompiliert werden. Es wird nur dann etwas ständig neu Kompiliert wenn man einen String einem eval() übergibt.

Ansonsten werden Annonyme Subroutinen genauso Kompiliert wie normale Subroutinen. Nur mit dem Vorteil das man die Kompilation auch zur Laufzeit aufschieben kann. Und dann den Subroutinen Namen dynamisch vergeben kann. Oder Generell ertsmal zur Laufzeit festlegen kann was die Subroutine überhaupt machen sollen. Und solche Spielerein.

Wenn man sich etwas mit AUTOLOAD oder dem Exporter Modul beschäftigt dann sollte man Wissen wie das geht.\n\n

<!--EDIT|sid burn|1177701602-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
PerlProfi
 2007-04-27 23:23
#76298 #76298
User since
2006-11-29
340 Artikel
BenutzerIn
[default_avatar]
Ich dachte eigentlich, dass bei jedem Aufruf einer Subroutine, die selbst anon. Subroutinen enthällt, diese auch kompiliert werden müssen, die Variable wird ja jedesmal neu zugeordnet, oder nicht ?
sid burn
 2007-04-27 23:42
#76299 #76299
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=PerlProfi,27.April.2007, 21:23]Ich dachte eigentlich, dass bei jedem Aufruf einer Subroutine, die selbst anon. Subroutinen enthällt, diese auch kompiliert werden müssen, die Variable wird ja jedesmal neu zugeordnet, oder nicht ?[/quote]
Kommt auf den Geltungsbereich der Variable drauf an ob sie neu Kompiliert wird oder nicht. Ansonsten unterscheidet sich eine Anonyme Subroutine nicht sehr stark von einer normalen Subroutine.

Wenn du eine Subroutine anlegst dann wird ein Eintrag in der Symboltabelle erzeugt. In der Symboltabelle existiert ein Typeglob. Ein Typeglob wiederrum ist nichts anderes als ein Perl Hash. Und dieser Perl HASH hat Keys. Und ein spezieller Key zeigt auf die Subroutine.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/perl
use strict;
use warnings;

sub hallo {
print "Hallo, Welt!\n";
}

my $blub = *hallo{CODE};
$blub->();


Der KEY für den Subroutinen Teil heißt CODE. Daher wenn du eine Subroutine erzeugt hast, kannst du aus den Typeglob einfach den Key CODE nehmen, dieser enthält eine Referenz auf die Subroutine. Und dieser kannst nach belieben einer neuen variable zuweisen.

Naja, und wenn du es so siehst. Wo ist da noch der Unterschied wenn du eine Anonyme Subroutine hast?

Eine Annonyme Subroutine ist nichts anderes als eine Referenz auf einen Code Stück. Das ist genau das was bei Perl die Symboltabelle ist.

Ansonsten wenn du eine Anonyme Subroutine in einer Funktion erzeugst dann muss diese Subroutine sicherlich erstmal zur Laufzeit neu Kompiliert werden. Klar in dem Punkt hast du recht. Wenn das ganze aber fertig ist, und du gibst dann eine Referenz in deiner Funktion zurück, dann wird die erstellte Subroutine ja nicht vernichtet. Du hast dann eine fertige Subroutine die aber nicht bei jedem Aufruf neu Kompiliert wird.

Ansonsten um 100%ig Korrekt zu sein, wird die Subroutine auch nicht wirklich komplett zur Laufzeit Kompiliert. Das passiert nur bei einem String eval. Wenn du allerdiengs eine Anonyme Subroutine schreibst und das Ergebnis einen Typeglob zuweist dann passiert die Kompilation trotzdem zur Kompilierzeit, aber die Zuweisung wie du die Funktion ansprichst wird einmalig erst zur Laufzeit festgelegt.

Oder es wird erst zur Laufzeit festgelegt welche Variablen du nutzt.

Beim ersten mal entsteht also sicherlich zur Laufzeit einen Overhead, aber nur beim ersten Aufruf, bzw erzeugen. Danach niht mehr. Komplett Kompilieren zur Laufzeit macht man nur mit einem String eval().

Ansonsten wenn du eine Referenz auf eine erstellte Subroutine zurück gibst dann lebt diese ja beim Verlassen der Subroutine weiterr weil da immer noch eine Referenz drauf zeigt. Ist letztendlich das selbe als wenn du in einer Subroutine eine Datenstruktur erzeugst und dann eine Referenz auf diese Datenstruktur zurück gibst. Diese überlebt dann auch noch weiter, und wird erst dann Zerstört wenn der Refrenzcounter auf 0 geht.

Solange also deine Variable lebt und da etwas auf den Code zeigt wird deine Subroutine nicht zerstört und auch nicht ewig neu Kompiliert.

Auser natürlich deine variable verlässt den Gültigkeitsbereich, und du rufst die Subroutine neu auf die dir dann wieder die Subroutine erstellt.



Nützlich ist das ganze wie bereits gesagt für Closures. Z.B in der OOP kannst du das auch gebrauchen um z.B. Klassenmethoden zu erzeugen die ihre Werte Speichern, aber ohne das package Variablen benutzt werden die Öffentlich zugänglich sind.

Oder du kannst Funktionen damit erzeugen die bei jedem Aufruf etwas anderes zurück geben. So wie es rand() z.B. macht. Ist ja alles letztendlich nichts anderes als eine Closure. ;)\n\n

<!--EDIT|sid burn|1177703781-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
PerlProfi
 2007-04-28 00:34
#76300 #76300
User since
2006-11-29
340 Artikel
BenutzerIn
[default_avatar]
Danke für die ausführliche Erklärung mit der Symboltabelle.
Aber:
Quote
Solange also deine Variable lebt und da etwas auf den Code zeigt wird deine Subroutine nicht zerstört und auch nicht ewig neu Kompiliert.

Auser natürlich deine variable verlässt den Gültigkeitsbereich, und du rufst die Subroutine neu auf die dir dann wieder die Subroutine erstellt.

Da liegt ja gearde meine Frage, wenn ich folgenden code habe:
Code: (dl )
1
2
3
4
sub aussen
{
my $innen = sub { 1...10 };
} # aussen

Wird dann jedesmal wenn ich aussen() aufrufe die innere Subroutine - $innen - neu kompiliert ?
So wie ich das verstanden habe schon, aber wie kann ich denn jetzt Modularisieren ohne das mir das passiert ?

MfG
Sucher
 2007-04-28 02:31
#76301 #76301
User since
2007-03-26
47 Artikel
BenutzerIn
[default_avatar]
Hallo,
wenn du eine anonyme subroutine privat in einem begrenzten Scope haben willst, dann erzeuge doch einfach einen begrenzten Scope (oder verstehe ich das Problem nicht?):


Code: (dl )
1
2
3
4
5
6
7
{
my $innen = sub { #irgendwas};
sub aussen
{
$innen->();
} # aussen
}


Grüße,
sid burn
 2007-04-28 02:59
#76302 #76302
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=PerlProfi,27.April.2007, 22:34]Danke für die ausführliche Erklärung mit der Symboltabelle.
Aber:
Quote
Solange also deine Variable lebt und da etwas auf den Code zeigt wird deine Subroutine nicht zerstört und auch nicht ewig neu Kompiliert.

Auser natürlich deine variable verlässt den Gültigkeitsbereich, und du rufst die Subroutine neu auf die dir dann wieder die Subroutine erstellt.

Da liegt ja gearde meine Frage, wenn ich folgenden code habe:
Code: (dl )
1
2
3
4
sub aussen
{
my $innen = sub { 1...10 };
} # aussen

Wird dann jedesmal wenn ich aussen() aufrufe die innere Subroutine - $innen - neu kompiliert ?
So wie ich das verstanden habe schon, aber wie kann ich denn jetzt Modularisieren ohne das mir das passiert ?

MfG[/quote]
Ja $innen wird immer neu zugewiesen. Allerdiengs in diesem beispiel nicht 100%ig alles neu Kompiliert. Was da jetzt genau abläuft weiß ich nicht genau. Aber in dem beispiel ist dein Code ja fest und es ändert sich nicht. Daher wird dein Code schon vorher Kompiliert, du hast nur ein Overhead weil du jedesmal den Code wieder einer Variablen zuweist.

Richtig Kompiliert würde nur dann, wenn du ein
Code: (dl )
eval "sub { 1 .. 10 }"
machst, denn dann wird der String erst wirklich zur Laufzeit Kompiliert und ausgewertet.

Das bei einen normalen sub ohne eval() schon etwas zur Laufzeit passiert kannst du auch daran sehen wenn du Syntaxfehler einbaust, den die werden schon vor dem Ausführen deines Skriptes abgefangen.


Ansonsten ist dein Beispiel ja ziemlich nutzloß. Deswegen kann ich nicht sagen wie du es jetzt Modulasieren solltest. Es hängt davon ab was du machen möchtest.

Möchtest du eine Sub einfach nur in einer Variable nutzen kannst du gleich das hier machen:
Code: (dl )
1
2
3
4
5
package test;
my $function = sub { 1 .. 10 };

# später...
$function->();


Ansonsten kannst du die Technik auch nutzen für Closures. Wenn du Klassenmethoden schreiben möchtest die dir z.B. ausgeben wieviele Klassen du erzeugt hast.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test;
my $counter_incr;
my $counter_decr;
{
my $counter = 0;
$counter_incr = sub { $counter++ };
sub get_counter {
return $counter;
}
$counter_decr = sub { $counter-- };
}

sub new {
# Objekt erzeugen
$counter_incr->();
}

sub DESTROY {
$counter_decr->();
}


Damit hast du eine Closure und zwei Funktionen die nur in der Datei gültig sind. In der Datei sollte ja üblicherweise nur die Klasse sein.

Damit ist es nicht Möglich das jemand fremdes die Klassenmethoden von hand aufrufen kann, und den counter irgendwie Manipulieren kann.

Auch für das automatische erzeugen von get oder set Accessors kannst du das ganze nutzen.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package test;
use vars '$AUTOLOAD';

sub AUTOLOAD {
my $method = $AUTOLOAD;
$method =~ s/.*:://;

if ( my $attr = $method =~ m/^get(_.*)/ ) {
no strict 'refs';
*{__PACKAGE__ . "::get$attr"} = sub {
my ($self) = @_;
return $self->{$attr};
};
goto &$AUTOLOAD;
}
}


Wenn dein Object z.B. auf ein hash basiert und alle Keys z.B. folgendermaßen benannt sind:

_title
_subtitle
_text

etc. dann brauchst du jetzt z.B. keine get Methoden mehr schreiben. Wenn du ein Objekt nutzt und das erstemal die Funktionen aufrufst:

get_title()
get_subtitle()
get_text()

dann werden Sie erzeugt. bzw. Sie existieren ja noch nicht, und werden dann von AUTOLOAD erzeugt. Allerdiengs werden Sie nur beim ersten Aufruf erzeugt. Danach bei jedem weiteren Aufruf existieren die Methoden bereits, und müssen nicht mehr erneuert erzeugt werden.

Naja du musst sicherlich noch mehr machen für ein Korrektes Beispiel und keines der Beispiel soll oben vollständig sein, ich hoffe nur die Sachen die man damit machen kann sind erklärend genug.

Normalerweise müsstest du noch eine Liste bereit stellen welche Attribute du hast, damit nicht gleich für alles was mit "get_" anfängt auch gleich eine Methode erstellt wird, obwohl du gar nicht so ein Attribut anbietest.

Und DESTROY muss man entweder auch in AUTOLOAD abfangen, oder am besten zumindest eine DESTROY Methode erstellen die mindestens

"$self->SUPER::DESTROY()" aufruft.
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
<< |< 1 2 3 >| >> 21 Einträge, 3 Seiten



View all threads created 2007-04-26 20:29.