Schrift
[thread]8523[/thread]

eval is evil: Willkommen auf der dunklen Seite?!

Leser: 3


<< |< 1 2 >| >> 20 Einträge, 2 Seiten
Ronnie
 2006-11-22 21:21
#71914 #71914
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
Hallo miteinander,

nachdem ich lange eval missachtet habe, aus den bekannten Gründen - wollte ich jetzt ein wenig damit experimentieren (nachdem ich in fremden Gewässern gefischt habe). Mein erster Versuch scheiterte aber bereits direkt:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

package Foo;

sub new {
my $class = shift;
my $self = { items => \@_ };
return bless $self, $class;
}

sub add_method {
my $self = shift;
my $name = shift;
die unless $name and $name =~ /^\w+$/;
eval (
sub $name {
my $self = shift;
my $item = $name;
return [ @{$self->{items}}, $item ];
}
);
}

package main;

my $foo = Foo->new(qw/foo bar/);
print Dumper $foo;

$foo->add_method('buz');
print Dumper $foo->buz;

Ich wollte Methoden on-the-fly erstellen, Perl ist aber dagegen:
Illegal declaration of anonymous subroutine

Habt ihr schon mal was in der Richtung gemacht und gibt es Empfehlungen wo man etwas über Meta-Programming in Perl findet?

Natürlich geht folgende Variante, aber die finde ich im Ergebnis nicht so schön:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
sub add_lambda {
my $self = shift;
my $name = shift;
die unless $name and $name =~ /^\w+$/;
$self->{$name} = sub {
my $self = shift;
my $item = $name;
return [ @{$self->{items}}, $item ];
}
}

Vorschläge?

Gruß,
Ronnie
ptk
 2006-11-22 22:41
#71915 #71915
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
Deine Syntax ist falsch: entweder du benutzt eval { ... } für das nicht-so-evil eval oder ein *String*argument.
betterworld
 2006-11-22 22:41
#71916 #71916
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Eval bekommt einen String als Parameter. Du hast also ein qq vor (...) vergessen.\n\n

<!--EDIT|betterworld|1164228181-->
Ronnie
 2006-11-22 23:06
#71917 #71917
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
Danke, das behebt zumindest das formale Problem. Trotzdem kann es so nicht funktionieren - es ist also eher Pseudocode, da Perl nicht klar sein kann was zu diesem Zeitpunkt definiert werden soll und was erst beim späteren Aufruf der Methode definiert ist, einfach weil unklar ist welche Variablen in einem interpolativen Kontext sind und welche es nicht sein sollen. Ich denke ich komme um ein wenig Forschungsarbeit nicht drumrum.
pq
 2006-11-22 23:25
#71918 #71918
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
@ronnie: was genau willst du denn machen& wofuer brauchst du
das eval ueberhaupt?
du willst eine subroutine on-the-fly erstellen und ihr einen namen geben?
Code: (dl )
1
2
3
4
5
6
7
my $name = "foobar";
my $sub = sub {
 blabla
};
no strict 'refs';
*{$name} = $sub;
foobar();
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
 2006-11-22 23:40
#71919 #71919
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Ich habe das Buch "Fortgeschrittene Perl Programmierung". 1.Auflage (nicht zweite). Das Buch ist zwar schon alt und von 1998 aber durch dieses Buch muss ich sagen lernt man richtig die Vorzüge von eval, und er zeigt auch was alles so Möglich. Klar "kann" eval() böse sein, aber mitlerweile finde ich das man damit mehr Sinvolle Sachen tun kann. Und ich bin auch nicht mehr der Auffassung das es Grundlegend Böse ist.

Ansonsten wenn du eine Subroutine in einem String schreibst, dann musst du daerstmal daran denken das dies erstmal ein normaler String ist, wo auch Variableninterpolation stattfinden kann. Wenn du also eine Subroutine erzeugen möchtest die nachher den wirklichen Perlcode "my $var = shift" ausführt, musst du beachten das du das Dollarzeichen bei $var escapest. Wenn du es nicht machst dann würde vor dem eval noch versucht werden $var im String zu interpolieren, und das ergebnis führst du dann erst aus. Und das willst du wahrscheinlich nicht.

Ansonsten wenn dein Ablauf im Programm identisch ist, und sich nur Funktionsname oder Variablennamen ändern dann würde es sich viel mehr Lohnen wenn du einfach die Symboltabelle bearbeitest. Das hat den Vorteil meiner Meinung nach das es besser lesbar ist. Andere Vorteile sind das die Syntax auch weitestgehenst schon zur Compilierzeit überprüft wird, und bei String eval geschieht ja alles nur komplett zur Laufzeit.

In einem anderen Forum hatte ich mal ein kleines Beispiel für eval gegeben wie sich noch Performance heraus schlagen liese. Vielleicht hilft dir dieses kleine Beispiel etwas weiter.

Wenn du es das Programm ausführst dann siehst du welchen Perl Code ich in $code zusammengebaut habe. Wenn ich diesen Code ausführen möchte, dann müsste man einfach nur "eval $code;" schreiben.

Du musst eigentlich nur dran denken das du es mit einem String zu tun hast, und die richtigen Zeichen escapen musst.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
my @regexe = (
qr/Ausdruck0/,
qr/Ausdruck1/,
qr/Ausdruck2/,
qr/Ausdruck3/,
);

my @zaehler;

my $code = "while ( my \$zeile = <SUCHE> ) {\n";
for my $i ( 0 .. $#regexe ) {
$code .= "\tif ( \$zeile =~ m/$regexe[$i]/ ) { \$zaehler[$i] }\n";
}
$code .= "}\n";

print $code;
\n\n

<!--EDIT|sid burn|1164231805-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
Ronnie
 2006-11-22 23:42
#71920 #71920
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
@pq: Ich habe das bei Beispiel-Codes aus dem Ruby-Cookbook gesehen - es ging um Metaprogramming. Ich fand die Idee interessant. Über die weitere Anwendung habe ich mir noch nicht viel Gedanken gemacht, habe aber ein paar Ideen die in Richtung deklarativen Sprachgebrauchs gehen.

Ruby erlaubt Methoden zur Laufzeit zu generieren und bietet auch Hook-Methoden wie undefined_method die aufgerufen werden wenn keine passende Methode existiert, was man wiederum nutzen kann um diese bei Bedarf zu generieren. Ich bin noch nicht wirklich weit mit dem Thema, habe aber eben schon überlegt wie es auf Perl transferierbar sein könnte. Ich will wenn ich das Buch durchhabe ein paar alte Applikationen nach Ruby konvertieren, schauen was an neuen Ideen dazu kommt und diese dann zurück in die Perl-Varianten fließen lassen. Ich habe festgestellt das ich ganz gut neue Ideen verinnerliche wenn ich ab und an mal die Perspektive wechsle.

Ich fürchte in diesem speziellen Fall war ich etwas zu übereifrig und habe zu wenig vorher drüber nachgedacht.

Gruß,
Ronnie
Ronnie
 2006-11-22 23:47
#71921 #71921
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
[quote=sid burn,22.11.2006, 22:40]Du musst eigentlich nur dran denken das du es mit einem String zu tun hast, und die richtigen Zeichen escapen musst.[/quote]
@sid burn: Danke, so funktioniert es. Mal sehen was man daraus machen kann!
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
sub add_method {
my $self = shift;
my $name = shift;
die unless $name and $name =~ /^\w+$/;
eval qq(
sub $name {
my \$self = shift;
my \$item = $name;
return \[ \@{\$self->{items}}, \$item \];
}
);
}
sid burn
 2006-11-22 23:58
#71922 #71922
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Quote
Ruby erlaubt Methoden zur Laufzeit zu generieren und bietet auch Hook-Methoden wie undefined_method die aufgerufen werden wenn keine passende Methode existiert, was man wiederum nutzen kann um diese bei Bedarf zu generieren. Ich bin noch nicht wirklich weit mit dem Thema, habe aber eben schon überlegt wie es auf Perl transferierbar sein könnte.

Dafür kannst du doch die Subroutine AUTOLOAD benutzen.
Immer wenn eine Funktion/Methode nicht existiert wird die Subroutine AUTOLOAD aufgerufen.

Wenn du in diesem Package noch die Globale Variable $AUTOLOAD anlegst, dann enthält diese Variable den kompletten Funktionaufruf. In der Funktion AUTLOAD enthält @_ alle Paremeter mit der du deine Funktion aufgerufen hast.

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

our $AUTOLOAD;
sub AUTOLOAD {
$" = ', ';
print "Es wurde [$AUTOLOAD] mit den Parametern [@_] aufgerufen\n";
}

hallo();
welt( 'foo' );
wie( 'bar', 'shoe' );


Bei der Ausführung kommt dann folgendes heraus

Code: (dl )
1
2
3
Es wurde [main::hallo] mit den Parametern [] aufgerufen
Es wurde [main::welt] mit den Parametern [foo] aufgerufen
Es wurde [main::wie] mit den Parametern [bar, shoe] aufgerufen


Wenn du nun innerhalb der Funktion AUTOLOAD die Subroutine mit String eval erzeugst dann hast du deine erstellte Methode. Nachdem die Methode einmal in dem package existiert wird danach ja die Methode aufgerufen. Und nicht mehr die AUTOLOAD Subroutine.

Das einzige worauf do achten musst ist auf die Methode DESTROY. Entweder brichst du bei der Methode ab, oder du definierst eine leere Methode DESTROY.

Am besten vor allem bei der OOP ist es immer eine Methode DESTROY zu erstellen die folgendes beinhaltet.
Code: (dl )
$self->SUPER::DESTROY()

Wenn du es nicht machst kann es zur Folge haben das die Vererbung mit der Methode nicht mehr klappt.


Ganz zum Schluss musst du nur beachten das du die Methode auch noch in AUTOLOAD aufrufst, sonst erstellst du sie zwar beim ersten Aufruf, führst sie aber nicht aus.

Von daher reicht es nach dem erstellen die Methode einfach aufzurufen "erstellte_methode(@_)". @_ enthält ja alle Parameter mit der du deine Methode aufgerufen hast.

Besser und so steht es im Buch ist aber "goto &erstellte_ethode". Das soll den Vorteil haben das wenn du das Programm irgendwie pofilierst (wie auch immer das heißt) das der Aufruf von AUTOLOAD nicht sichtbar ist. Mit goto wird die aktuelle Funktion durch eine andere ersetzt. Weiterhin wird die Funktion automatisch mit den Parameter von @_ aufgerufen.\n\n

<!--EDIT|sid burn|1164233722-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
Ronnie
 2006-11-23 00:18
#71923 #71923
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
@sid burn: Danke, super - das ist der nächste Schritt. An GOTO hätte ich auch nicht mehr gedacht - auch wenn es mir schon mal über den Weg gelaufen ist, da es die vernünftigste Möglichkeit darstellt Endrekursion in Perl zu nutzen. Vielen Dank!
<< |< 1 2 >| >> 20 Einträge, 2 Seiten



View all threads created 2006-11-22 21:21.