eval a string with a hash ref?
|
|
Thread rating:  |
James Reynolds - 12 Nov 2005 07:56 GMT Is there someway to execute this code like this:
$subroutine_name = "something"; # can't change $hashref->{'key'}='value'; # can't change
eval "$subroutine_name($hashref)"; # how do I eval this? It doesn't eval.
Essentially I get the subroutine names to execute from a text file, and there will be thousands of differently named subroutines. I simplified the reason why the hash exists in this example. Needless to say I can't get out of using it that way easily, if at all.
--
Thanks,
James Reynolds University of Utah Student Computing Labs james@scl.utah.edu 801-585-9811
Boysenberry Payne - 12 Nov 2005 10:05 GMT I could only get it to work as a method:
sub new { my $class = shift; my $self = {@_}; bless( $self, $class ); }
sub tester { my $self = shift; my $hash = shift if @_; if( $hash ) { print "$hash->{key}\n"; } else { print "No Hash\n"; } }
my $sub_ref = new;
my $subroutine_name = "tester"; # can't change my $hashref = { key => "value" }; # can't change
eval { $sub_ref->$subroutine_name( $hashref ) };
Boysenberry
boysenberrys.com | habitatlife.com | selfgnosis.com
> $subroutine_name = "something"; # can't change > $hashref->{'key'}='value'; # can't change > > eval "$subroutine_name($hashref)"; # how do I eval this? It doesn't > eval. Sherm Pendley - 12 Nov 2005 11:42 GMT > Is there someway to execute this code like this: > [quoted text clipped - 9 lines] > Needless to say I can't get out of using it that way easily, if at > all. Block eval() is useful for exception handling, but string eval() is just evil misspelled.
Use a dispatch table instead:
#!/usr/bin/perl
use strict; use warnings;
my %dispatch = ( 'a' => \&a, 'b' => \&b, 'c' => \&c, );
my $hashref = { 'foo' => 1, 'bar' => 2, 'baz' => 3, };
for my $sub_name ('a', 'b', 'c') { $dispatch{$sub_name}->($hashref); }
sub a { my ($hr) = @_; print "A called\n"; print "\t", join(",", keys(%$hr)), "\n"; }
sub b { my ($hr) = @_; print "B called\n"; print "\t", join(",", keys(%$hr)), "\n"; }
sub c { my ($hr) = @_; print "C called\n"; print "\t", join(",", keys(%$hr)), "\n"; }
Have a look in the archives for comp.lang.perl.misc at Google Groups - the subject of "dispatch tables" has been covered at great length:
http://groups.google.com/group/comp.lang.perl.misc/search? group=comp.lang.perl.misc&q=dispatch+table
sherm--
Cocoa programming in Perl: http://camelbones.sourceforge.net Hire me! My resume: http://www.dot-app.org
Ken Williams - 12 Nov 2005 22:05 GMT > Is there someway to execute this code like this: > [quoted text clipped - 3 lines] > eval "$subroutine_name($hashref)"; # how do I eval this? It doesn't > eval. You want:
eval "$subroutine_name(\$hashref)";
-Ken
Randal L. Schwartz - 12 Nov 2005 22:12 GMT >>>>> "Ken" == Ken Williams <ken@mathforum.org> writes: Ken> You want:
Ken> eval "$subroutine_name(\$hashref)";
Only if you also want "slow" and "dangerous'. See the other answers in this thread for safer faster solutions.
print "Just another Perl hacker,"; # the original
 Signature Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 <merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/> Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Ken Williams - 13 Nov 2005 04:18 GMT >>>>>> "Ken" == Ken Williams <ken@mathforum.org> writes: > [quoted text clipped - 4 lines] > Only if you also want "slow" and "dangerous'. See the other answers > in this thread for safer faster solutions. Yeah, I know, but I posted it because I didn't really like the other solutions; while they use "better" (and more complicated) techniques, they won't actually solve the OP's problem. The "pretend you have methods instead of subroutines" solution is just wishful thinking, and the "dispatch tables" solution seems overengineered and won't work unless the specific list of allowed functions (rather than just a naming-scheme pattern or similar) is known to the dispatcher.
James, if you don't want to use an eval, and you need to call functions by name this way, you can just use a symbolic reference:
&{$subroutine_name}($hashref);
If "use strict;" is in effect (I'm guessing it's not ;-), then preface that line with "no strict qw(refs);" or else it'll blow up.
Note that under the hood this is a dispatch table just like Sherm's solution, but it uses Perl's symbol tables for dispatch rather than a new hash.
I'm assuming you're being careful enough in this code that someone can't come along and call some function that you don't want them to call.
-Ken
Boysenberry Payne - 13 Nov 2005 06:16 GMT > The "pretend you have methods instead of subroutines" solution is just > wishful thinking, Just out of curiosity, why is this wishful thinking?
Thanks, Boysenberry
boysenberrys.com | habitatlife.com | selfgnosis.com
Ken Williams - 13 Nov 2005 21:22 GMT >> The "pretend you have methods instead of subroutines" solution is >> just wishful thinking, > > Just out of curiosity, why is this wishful thinking? Because he doesn't have methods, he has functions. They're expecting one argument, the hash ref, not an additional $self or $class argument at the beginning. In order to use this solution he'd have to retool his entire code base of functions to make them methods instead.
-Ken
David Cantrell - 14 Nov 2005 14:58 GMT > Because he doesn't have methods, he has functions. They're expecting > one argument, the hash ref, not an additional $self or $class argument > at the beginning. In order to use this solution he'd have to retool > his entire code base of functions to make them methods instead. It's entirely possible to write your subroutines so that they'll work just fine as either functions *or* class methods *or* instance methods. The code is a bit ghastly though, and I don't recommend it. In fact, I'm taking that particular breed of insanity out of one of my modules that is on the CPAN. Thankfully, I never documented that they could be called as functions, and so I don't feel that I shuold care if their removal breaks anything :-)
 Signature David Cantrell | Nth greatest programmer in the world
One person can change the world, but most of the time they shouldn't -- Marge Simpson
Randal L. Schwartz - 15 Nov 2005 11:11 GMT >>>>> "David" == David Cantrell <david@cantrell.org.uk> writes: David> It's entirely possible to write your subroutines so that they'll work David> just fine as either functions *or* class methods *or* instance methods. David> The code is a bit ghastly though, and I don't recommend it. In fact, David> I'm taking that particular breed of insanity out of one of my modules David> that is on the CPAN. Thankfully, I never documented that they could be David> called as functions, and so I don't feel that I shuold care if their David> removal breaks anything :-)
Even if you fix yours, CGI.pm will live on as a public example of how twisted code can get over the years. :)
See the horrors of &self_or_default, but only when properly prepared to flush your mind later.
 Signature Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 <merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/> Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Ted Zeng - 30 Nov 2005 18:31 GMT Hi,
I have been using Time::localtime
Call.
Now I want to use POSIX module. But There is also a Lcoaltime Call in this module.
What can I do to still use Time::localtime And also the POSIX module?
Ted zeng Adobe Systems
Sherm Pendley - 30 Nov 2005 18:56 GMT > I have been using > Time::localtime [quoted text clipped - 9 lines] > Time::localtime > And also the POSIX module? Use import lists.
From the Time::localtime module docs:
> To access this functionality without the core overrides, pass the > use an empty import list, and then access function functions with > their full qualified names. On the other hand, the built-ins are > still available via the CORE:: pseudo-package. And, from the POSIX docs:
> Everything is exported by default with the exception of any POSIX > functions with the same name as a built-in Perl function, such as > abs, alarm, rmdir, write, etc.., which will be exported only if you > ask for them explicitly. This is an unfortunate backwards > compatibility feature. You can stop the exporting by saying use > POSIX () and then use the fully qualified names (ie. POSIX::SEEK_END). So, in your code you could do something like this:
use POSIX; # Default imports use Time::localtime qw(); # Empty import list, so no overrides
my $p_time = localtime(); # calls localtime() imported from POSIX my $time_object = Time::localtime::localtime();
sherm--
Cocoa programming in Perl: http://camelbones.sourceforge.net Hire me! My resume: http://www.dot-app.org
Ted Zeng - 30 Nov 2005 23:22 GMT Thanks. It works.
Ted zeng
Sherm Pendley - 13 Nov 2005 07:22 GMT > Yeah, I know, but I posted it because I didn't really like the > other solutions; while they use "better" (and more complicated) [quoted text clipped - 4 lines] > functions (rather than just a naming-scheme pattern or similar) is > known to the dispatcher. The subroutines have to be known anyway. Unless, of course, you're going to let the eval() just crash...
> James, if you don't want to use an eval, and you need to call > functions by name this way, you can just use a symbolic reference: > > &{$subroutine_name}($hashref); When in doubt, benchmark it:
#!/usr/bin/perl
use strict; use warnings;
use Benchmark qw(:all);
my %dispatch = ( 'a' => \&a, 'b' => \&b, 'c' => \&c, );
my $hashref = { 'foo' => 1, 'bar' => 2, 'baz' => 3, };
timethese(100000, {
'Dispatch Table' => sub { for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') { $dispatch{$sub_name}->($hashref); } },
'Symref' => sub { for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') { { no strict 'refs'; &$sub_name($hashref); } } },
'Eval' => sub { for my $sub_name ('a', 'b', 'c', 'b', 'c', 'a', 'a', 'c', 'b') { eval "$sub_name(\$hashref)"; } },
});
sub a { my ($hr) = @_; my $foo = "\t" . join(",", keys(%$hr)) . "\n"; }
sub b { my ($hr) = @_; my $foo = "\t" . join(",", keys(%$hr)) . "\n"; }
sub c { my ($hr) = @_; my $foo = "\t" . join(",", keys(%$hr)) . "\n"; }
The results I get are:
Benchmark: timing 100000 iterations of Dispatch Table, Eval, Symref... Dispatch Table: 17 wallclock secs (14.77 usr + 0.29 sys = 15.06 CPU) @ 6640.11/s (n=100000) Eval: 155 wallclock secs (139.45 usr + 3.23 sys = 142.68 CPU) @ 700.87/s (n=100000) Symref: 18 wallclock secs (17.49 usr + 0.35 sys = 17.84 CPU) @ 5605.38/s (n=100000)
The Eval results are no surprise - it has to compile the string for each iteration, which is a huge performance hit.
The symref approach has some overhead too, I suppose because of the extra scoping block for "no strict", but it's relatively small. Still, it's enough to make using symrefs a questionable idea.
I have to admit, I'm puzzled about why you'd call the dispatch table "over engineered". There's a difference in syntax when the sub is called, but that's trivial. The only substantial difference is the addition of the dispatch table itself, which isn't exactly a herculean effort. The result is both the cleanest and fastest of the three approaches.
sherm--
Cocoa programming in Perl: http://camelbones.sourceforge.net Hire me! My resume: http://www.dot-app.org
Ken Williams - 13 Nov 2005 21:34 GMT >> Yeah, I know, but I posted it because I didn't really like the other >> solutions; while they use "better" (and more complicated) techniques, [quoted text clipped - 6 lines] > The subroutines have to be known anyway. Unless, of course, you're > going to let the eval() just crash... They have to *exist*, but they don't have to be known to the dispatcher.
> When in doubt, benchmark it: > [...] [quoted text clipped - 5 lines] > Symref: 18 wallclock secs (17.49 usr + 0.35 sys = 17.84 CPU) @ > 5605.38/s (n=100000) Yeah, that seems about right.
> The Eval results are no surprise - it has to compile the string for > each iteration, which is a huge performance hit. > > The symref approach has some overhead too, I suppose because of the > extra scoping block for "no strict", but it's relatively small. Still, > it's enough to make using symrefs a questionable idea. Well, symrefs do start out as a questionable idea in the first place. It's old-school programming reminiscent of "goto" and "poke". But it seemed like exactly what James was looking for - he had the name of a function in a variable and wanted to call it. So rather than treat this as an x-y problem, I decided to just answer the question straightforwardly.
-Ken
Joel - 14 Nov 2005 04:07 GMT >> Yeah, I know, but I posted it because I didn't really like the other >> solutions; while they use "better" (and more complicated) techniques, [quoted text clipped - 6 lines] > The subroutines have to be known anyway. Unless, of course, you're > going to let the eval() just crash... So, if one were implementing, say, FORTH, in Perl, intending to allow the user to compile subroutines of her own, can it be done with dispatch tables? Can you add entries to dispatch tables at run-time?
Sherm Pendley - 14 Nov 2005 19:54 GMT On Nov 13, 2005, at 11:07 PM, joel wrote:
> So, if one were implementing, say, FORTH, in Perl, intending to > allow the user to compile subroutines of her own, can it be done > with dispatch tables? Can you add entries to dispatch tables at run- > time? Sure - compiling code at run-time is what string eval() is for.
You could compile an anonymous sub to get a code ref:
my $coderef = eval "sub { $string_full_of_code }";
You could then store the coderef in a dispatch table:
$dispatch_table{$function_name} = $coderef;
Or, you could add it directly to Perl's symbol table:
no strict 'refs'; *{ $package.'::'.$function_name } = $coderef;
sherm--
Cocoa programming in Perl: http://camelbones.sourceforge.net Hire me! My resume: http://www.dot-app.org
James Reynolds - 13 Nov 2005 09:51 GMT >>>>>>>"Ken" == Ken Williams <ken@mathforum.org> writes: >> [quoted text clipped - 13 lines] >functions (rather than just a naming-scheme pattern or similar) is >known to the dispatcher. As it turns out, I already had a list of allowed functions because I didn't want to be eval'ing things from the text files and have it error out (as I knew I would be building the functions over time and I know I will always be missing some as there are potentially thousands of them--each represents a Lego brick...). So the dispatch table worked fine.
Thanks!
--
Thanks,
James Reynolds University of Utah Student Computing Labs james@scl.utah.edu 801-585-9811
|
|
|