Re: Small examples of string eval
by gellyfish (Monsignor) on May 13, 2006 at 07:53 UTC
|
This is the only real example I could find in my own code, essentially it is a method on a base collection class to cause the collection to appear to be sorted on an arbitrary attribute (or attributes) of the objects in the collection. It uses eval to dynamically create a sort sub from a list of comparison specifications passed as an argument. Okay it doesn't fit the the 'small' criteria but I think it is illustrative of the kind of use string eval is good for:
sub sort
{
my ( $self, $sort_spec ) = @_;
my $sort_done = 0;
if ( defined $sort_spec )
{
my @sort_objs;
foreach my $obj ( @{$self->{_data}} )
{
my $sv = [];
push @{$sv}, $obj->factory_index();
foreach my $spec ( @{$sort_spec} )
{
my $meth = $spec->{Key};
push @{$sv}, $obj->$meth();
}
push @sort_objs, $sv;
}
my @comparisons = ();
my $index = 0;
foreach my $spec ( @{$sort_spec} )
{
$index++;
my $comp = '';
my ($left_arg, $right_arg) = ('$a','$b');
if ( exists $spec->{Direction} and $spec->{Direction} eq '-' )
{
( $left_arg, $right_arg) = ('$b', '$a');
}
if ( $spec->{Compare} eq 'cmp' || $spec->{Compare} eq '<=>' )
{
$comp = "${left_arg}->[$index] $spec->{Compare} ${right_arg}
+->[$index]";
}
else
{
$comp = "$spec->{Compare}(${left_arg}->[$index],${right_arg}-
+>[$index])";
}
push @comparisons, $comp;
}
if ( $index )
{
my $sub = join ' || ' , @comparisons;
$sub = "sub { $sub };";
*sortsub = eval $sub;
$self->{_sort_index} = [];
@{$self->{_sort_index}} = map { $_->[0] }
sort sortsub @sort_objs;
$sort_done = 1;
my $index = 0;
foreach my $obj ( $self->list() )
{
$obj->factory_index($index++);
}
}
}
return $self->{_sorted} = $sort_done;
}
/J\ | [reply] [d/l] |
|
You don't need a eval to do this, and it's easier to do without it. You basically want a higher order function that composes other functions, and all of the functions are going to get the same arguments.
Starting from the finished product, you might have a sort subroutine like this, where the return value is the first thing that returns true (which should be -1 or 1).
sub sortsub {
$a cmp $b ||
other_function( $a, $b ) ||
...
}
That's the same as writing it as a collection of subroutines that get arguments instead of using globals though:
sub sortsub {
string_compare( $a, $b ) ||
other_function( $a, $b ) ||
...
}
But I can rewrite that sortsub to take a list of subroutine references as its arguments. Now my sortsub goes through each subroutine and returns when it finds one that returns a true value. If it finds 0 (meaning that sub thought the elements were equal), it goes onto the next subroutine.
sub sortsub {
my @subs = @_;
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
}
I can't use this in sort yet because I can't pass it arguments. I can, however, modify it to return a subroutine reference I can use:
sub make_sort_sub {
my @subs = @_;
return sub {
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
};
}
Now, I basically make the reference then use it in my sort block.
my $sort_sub = make_sort_sub( @sub_references );
my @sorted = sort { $sort_sub->() } @stuff;
And here's a full demo, using it with numbers, then strings, then a user-defined sort subroutine. I don't need an eval for any of it.
#!/usr/bin/perl
# pre-defined common sort subroutines
my %Subs = (
numeric_descending => sub { $b <=> $a },
numeric_ascending => sub { $a <=> $b },
string_descending => sub { $b cmp $a },
string_ascending => sub { $b cmp $a },
case_insensitive => sub { "\L$a" cmp "\L$b" },
);
sub make_sort_sub {
my @subs = @_;
return sub {
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
};
}
# numbers
{
my @use_these_subs = map { $Subs{$_} } qw(numeric_descending);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( 1 10 11 100 2 12 21 3 31 300 4 5 6 66 7
+ 71 );
print "@sorted\n";
}
# strings
{
my @use_these_subs = map { $Subs{$_} } qw(case_insensitive string_asce
+nding);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( Fred fred FReD Barney barney Betty BETT
+Y );
print "@sorted\n";
}
# strings by length with user defined subroutine
{
my @use_these_subs = ( sub { length $a <=> length $b } );
push @use_these_subs, map { $Subs{$_} } qw(string_ascending);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( Fred fred FReD Barney barney Betty BETT
+Y );
print "@sorted\n";
}
| [reply] [d/l] [select] |
|
Thank you, this is what I was looking for. I have a similar experience with string eval, generating subroutines that access certain data fields, using a configuration file. This was done to improve performance, as the generated subroutines were fine-tailored to the data extraction task at hand (with completely non-generic statements like substr($str, 2, 13)).
Unlike what many think, although the string eval is dangerous, it is necessary to efficiently and generically implement certain things that are impossible without it. The terrific "Higher Order Perl" has a lot of examples for smart usage of eval, and I'll look there as well.
| [reply] |
Re: Small examples of string eval
by graff (Chancellor) on May 13, 2006 at 07:00 UTC
|
Teaching newbies to use eval? What a concept... (like playing with knives) -- well, it's sure to be valuable as a learning experience.
The one thing where eval is basically unavoidable is the use of a string variable within the "tr///" operator. It's simple, basic, and has a bunch of obvious uses in "real-life".
(update: forgot to include an example...)
my %charmap = ( a => 'B', c => 'D', ) # ... could come from a config f
+ile
my $old = join '', keys %charmap;
my $new = join '', values %charmap;
while (<>) {
eval "tr{$old}{$new}";
if ( $@ ) {
die "tr{$old}{$new} failed at line $.: $_";
}
print;
}
| [reply] [d/l] |
|
In my opinion, eval EXPR (as opposed to eval BLOCK) should be used only as a last resort. Your code is a prime example. It breaks as soon as someone places -, } or \ in %charmap. The following does not, and avoids eval EXPR entirely:
while (<>) {
s/(.)/exists $charmap{$1} ? $charmap{$1} : $1/seg;
print;
}
(No, the /e is not the same as eval EXPR. There's no dynamic code generation. /ee, on the other hand, would be the same as eval EXPR.)
| [reply] [d/l] [select] |
|
By newbies I don't mean people who've learned to program Pascal yesterday, but rather programmers from other languages who don't yet grok the concepts of runtime evaluation and code generation.
Thanks for the example, but it's not what I'm looking for. Your example is very idiomatic to Perl, I'd say it's a patch on a deficiency, because I have no trouble imagining tr/// implemented in such a way that allows the trick without eval.
What I'm looking for is more in the code-generation league. Generate some code with eval, on the fly, and call it when necessary.
| [reply] |
|
| [reply] |
Re: Small examples of string eval
by BrowserUk (Patriarch) on May 13, 2006 at 10:20 UTC
|
I have several scripts that take numeric arguments from the command line where is is extremely useful to be able to enter expressions instead of simple values.
#! perl -slw
use strict;
our $LIMIT = eval $LIMIT || 'somedefault';
This way I can specify
script -LIMIT=2**33
or
script -LIMIT=8*1024**3
Which beats the crap out of trying to remember
script -LIMIT=8589934592
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
|
|
Re: Small examples of string eval
by UnderMine (Friar) on May 13, 2006 at 09:13 UTC
|
An example you use every day an never know about it. The dynamic loading of DBD drivers from the DBI source code.
# --- load the code
my $driver_class = "DBD::$driver";
eval qq{package # hide from PAUSE
DBI::_firesafe; # just in case
require $driver_class; # load the driver
};
UnderMine | [reply] [d/l] |
Re: Small examples of string eval
by eyepopslikeamosquito (Archbishop) on May 13, 2006 at 12:37 UTC
|
Most Perl experts discourage the use of string eval. For example:
- merlyn in •Re^2: Dynamically constructed function calls states: Do not resort to eval-string if other means are available. You're firing up the compiler (slower than almost any other solution), and exposing yourself to hard to debug and hard to secure practices.
- TheDamian in Perl Best Practices in Chapter 8 has a guideline titled: Avoid string eval. Damian argues that string eval can be slow; produces run time warnings rather than more desirable compile time warnings; and code that generates other code tends to be harder to maintain. He further advises you to use anonymous subroutines and the CPAN Sub::Installer module when you have a need to create new subroutines that are built around some expression that the user supplies.
Update: In
this quiz of the week MJD states:
A good rule of thumb is that unless what you're trying
to do is most clearly described as "compile and run
arbitrary Perl code", it's probably a mistake to
use 'eval' to do it.
| [reply] |
|
But each of them knows when to eshew complicated eval-avoidance-schemes, in favour of simplicity.
merlyn See name_to_page() in CGI::Prototype::Hidden, new() in CGI::Prototype::Mecha, type() in File::Finder, the grammer for Inline::Spew.
TheDamian See import() in Attribute::Handlers, import() in Attribute::Types, install_dispatch() in Class::MultiMethods, initialise() & new() in Class::Std... I stopped here with D-Z to go.
Dominus memoize() in Memoize, in Memoize::AnyDBM_File, fill_in() inText::Template, TIEARRAY() in Tie::File.
And before anyone leaps to these authors defense, there is no need. String eval is a tool. Avoiding it where practical is common sense; eshewing at all costs is illogical.
I'm betting that Perl6 will retain string eval in some form.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
| [reply] |
|
He (TheDamian) further advises you to use anonymous subroutines and the CPAN Sub::Installer module when you have a need to create new subroutines that are built around some expression that the user supplies.
While Sub::Installer is certainly a good idea (insulating the user from the evils of symbol table manipulation, which is at least as dangerous as string eval in the 'wrong' hands). It's code can hardly be considered to contain
"Best Practices" IMO. It clearly abuses UNIVERSAL in a way which affects not only the code which uses it, but all other running Perl code. This is the offending code:
package UNIVERSAL;
use base 'Sub::Installer';
This would mean that every Perl object will respond true to the query $object->isa('Sub::Installer'). Again, IMO, that is not a Best Practice, but instead a really really bad idea.
Sorry, I am a little passionate about code which tramples over the Perl 5 object model like this.
But fear not! CPAN will save the day. As an alternative, I offer Sub::Install an excellent module which does not assert it's will upon your entire runtime.
| [reply] [d/l] [select] |
Re: Small examples of string eval
by tilly (Archbishop) on May 15, 2006 at 06:44 UTC
|
See Class::FlyweightWrapper for an example. And advantage over alternate methods of doing the same thing is that in stack backtraces the proxying code will show up with useful package names. | [reply] |
Re: Small examples of string eval
by ruzam (Curate) on May 14, 2006 at 03:16 UTC
|
I have two places where I use eval all the time. I think eval is the best choice in these situations, maybe someone has a better alternative (that doesn't require non-core modules).
1) retrieving stored hashes
I regularily used Data::Dumper to turn hashes into strings so they can be stored in a database for later retrieval. The string accurately represents the original hash with out my having to do a thing and it's safe to store anyway I have to, flat files, dbm, mysql. When the data is later retrieved, eval is the easiest way to get it back into a useful hash. Something like this:
use Data::Dumper;
$Data::Dumper::Indent = 0; # no spaces tabs or other fluff
$Data::Dumper::Purity = 1; # include code to recreate recursive refere
+nces
sub store_hash {
my $hash_ref = shift;
my $saved = Dumper($hash_ref);
# store $saved somewhere
}
sub get_hash {
# get $saved from somewhere
my $VAR1;
eval {$saved}
return $VAR1 ? $VAR1 : {};
}
1) wrapping DBI calls to capture errors. Far as I know this is the recommended (only?) way to do this.
sub selectrow_array { my $dbh = shift;
my $query = shift;
my @result;
eval { @result = $dbh->selectrow_array($query) };
return my_custom_error_handler("selectrow_array $query") if $@;
return @result;
}
More generically I guess you could say eval is the tool of choice when ever you have to call code beyond your control that may kill your script. | [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
Re: Small examples of string eval
by aufflick (Deacon) on May 15, 2006 at 07:08 UTC
|
Along the lines of other commenters, if your goal is to enlighten non-agile developers about the beauty of a flexible language and run-time environment like perl, eval EXPR is not the thing to do it with (IMHO).
eval EXPR is what I use in php or Sybase stored procedures, to make up for the lack of expressiveness in the language.
While eval EPXR has definite uses (such as the quoted example of dynamically loading packages), probably 90% of the times that eval EXPR is used in Perl code, faster and more safe code could be written to do the same thing using techniques such as those shown in other responses (such as closures/currying etc.).
In fact, if these developers are serious C or Java developers and talk about things like compiler optimisations then they are going to recognise most eval EXPR based Perl code as a weak solution to hard problems. It would be sad for that to be their first Perl experience as Perl offers such better solutions to those problems than does, say, C or Java. | [reply] |
Re: Small examples of string eval
by eyepopslikeamosquito (Archbishop) on May 15, 2006 at 09:30 UTC
|
A historic example of string eval that springs to mind is
Larry's famous rename script.
See also
Unix rename faq (a version of Larry's script is given at the end) and the Perl Cookbook, recipe 9.9.
And though these may set a poor example for newbies, please notice that
Acme::Bleach:
do {eval brighten $shirt; exit} unless dirty $shirt && not dress $shir
+t;
Acme::Buffy:
do {eval unslay $demon; exit} unless evil $demon && not punch $demon;
and Acme::EyeDrops:
eval eval '"'.
("\["^
'+'). ('['^')')
.('`'|')') .('`'|'.').
('['^'/'). ('{'^'[').''.
('\\'). '"'.('`'|"\(").(
'`'|'%') .('`'|',').('`'|','
).('`'| '/').('{'^'[').("\["^
',').('`'|'/').('['^')').('`'
|',').('`'|'$').'\\'.('\\').(
'`'|'.').'\\'.'"'.';'.('!'^'+'
).'"';$:='.'^'~';$~='@'|"\(";
$^=')'^'[';$/='`'|'.';$,= ((
(( '('))))^'}';$\= '`'| ((
'!') );$:=')' ^(( (
'}' ))); $~= '*'
|(( '`')) ;( $^
)=( '+') ^+ ((
(( ( (( ((
(( ( ( (
(( ( ( (
( '_' )
) )) )) )
)) )))) ) ))
)))) ;#;#
all rely on good ol' string eval to perform their magic. :-)
| [reply] [d/l] [select] |
Re: Small examples of string eval
by jwkrahn (Abbot) on May 13, 2006 at 23:40 UTC
|
| [reply] |
|
use Algorithm::Loops qw( NestedLoops );
use List::Util qw( reduce sum );
sub mul { return reduce { $a * $b } @_; }
NestedLoops(
[
map { [ 0..$n_ra->[$_] ] }
0..$#$n_ra
],
sub {
$prob_ra[sum @_] += mul
map { $f_raa->[$_]->[$_[$_]] }
0..$#_;
}
);
I suspect that
$n_ra->[X]
is equal to
$#{$f_raa->[X]}
in which case
map { [ 0..$n_ra->[$_] ] } 0..$#$n_ra
can be replaced with
map { [ 0..$#$_ ] } @$f_raa
| [reply] [d/l] [select] |
Re: Small examples of string eval
by DrHyde (Prior) on May 15, 2006 at 09:22 UTC
|
Here's an example I wrote. It's in use in production code. The method that contains this is a "helper" method which lives in its own package and is used in several classes. It mangles some paramaters and then:
- changes package to whatever class it was invoked from;
- calls a method in that class's superclass.
The reason I need to change package halfway through the method is, from perldoc perlobj:
It is important to note that "SUPER" refers to the super-class(es) of the current package
so instead of just doing this:
$class->SUPER::blah($hash_of_data);
I have to do this:
eval "
package $class;
\$class->SUPER::blah(\$hash_of_data);
";
Block eval won't cut it here, and while I'm sure plenty of people are going to bleat that this travesty just demonstrates that my design was wrong in the first place (and I'd agree) in the real world one doesn't always know what all the requirements are when one starts writing code. I decided it was better to write a couple of lines of ugly code - and comment what I was doing and why - than to spend literally days re-jigging everything else so that I could then write that tiny snippet of functionality more cleanly. | [reply] [d/l] [select] |
Re: Small examples of string eval
by DrHyde (Prior) on Jun 22, 2006 at 11:12 UTC
|
Here (String eval is cool) is a great example. It's not code generation. In fact, all it does is generate a value from another value. However, using string eval makes it really obvious how the two values are related and avoids several lines of opaque bit-banging. | [reply] |