Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

operator in a variable?

2 views
Skip to first unread message

Michael Roper

unread,
Aug 22, 2003, 4:54:20 AM8/22/03
to
Is it possible to store pieces of an expression in a variable that will get
expanded before the larger expression is evaluated? For example, could I
write something like: $result = $a $op $b; that would be evaluated as
$result = $a + $b;? Thanks.

Michael Roper


Sam Holden

unread,
Aug 22, 2003, 5:22:47 AM8/22/03
to

Yes, you can do it using eval, see "perldoc -f eval" for details.

However, that leads to security issues, and hard to find bugs. A far
better approach is to use a hash containing coderefs for each operator.

Something like:

my %ops = ( '+' => sub {return $_[0] + $_[1]},
'-' => sub {return $_[0] - $_[1]},
);

my $x = 123;
my $y = 456;

for my $op ('+', '-') {
my $result = $ops{$op}($x, $y);
print "$x $op $y = $result\n";
}

See "perldoc perlref" for details on code references.

--
Sam Holden

Janek Schleicher

unread,
Aug 22, 2003, 5:30:47 AM8/22/03
to

perldoc -f eval

E.g.
my $x = 4;
my $op = '+';
my $y = 5;

my $result = eval "$x $op $y";
print $result;


$a and $b are global variables, thus I switched to $x and $y.


May I ask what the problem is you need to evaluate operators for.
Allthough the above is an accurate way to solve the problem, it is only
rarely needed as there are often more secure, more failsafe and more
beautiful ways to solve the original problem.

eval STRING is one of the things that you usually won't use unless you
really want to use it :-)

Greetings,
Janek

Ted Zlatanov

unread,
Aug 22, 2003, 6:03:55 AM8/22/03
to
On Fri, 22 Aug 2003, bi...@kamelfreund.de wrote:

> my $result = eval "$x $op $y";
> print $result;

This won't work correctly for operators that modify either variable,
because you interpolate $x and $y also; you probably want

my $result = eval "\$x $op \$y";

Ted

Ted Zlatanov

unread,
Aug 22, 2003, 6:11:48 AM8/22/03
to
On 22 Aug 2003, sho...@flexal.cs.usyd.edu.au wrote:

> my %ops = ( '+' => sub {return $_[0] + $_[1]},
> '-' => sub {return $_[0] - $_[1]},
> );
>
> my $x = 123;
> my $y = 456;
>
> for my $op ('+', '-') {
> my $result = $ops{$op}($x, $y);
> print "$x $op $y = $result\n";
> }

In case the operator needs to modify $x or $y, I would do it thus:

$ops{'+'} = sub { my $xr = shift; my $yr = shift; return $$xr + $$yr; };

my $result = $ops{$op}->(\$x, \$y);

Ted

Tassilo v. Parseval

unread,
Aug 22, 2003, 6:52:49 AM8/22/03
to
Also sprach Ted Zlatanov:

It's not necessary. @_ contains aliases to the variables that were
passed to the functions so altering them will alter the original values
as well.

Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval

Brian McCauley

unread,
Aug 22, 2003, 9:05:53 AM8/22/03
to
Ted Zlatanov <t...@lifelogs.com> writes:

That uglyness is quite unnecessary. The elements of @_ are _alaises_,
not copies, of the aguments passed to a subroutine.

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\

Sam Holden

unread,
Aug 22, 2003, 10:51:07 AM8/22/03
to

$ops{'+='} = sub {$_[0] += $_[1]};
($x, $y) = (123, 456);
$ops{'+='}($x, $y);
print "$x\n";

Is the obvious example that shows you don't need to make the code
unintuitive even if you want such things.

--
Sam Holden

Francesc Guasch

unread,
Aug 22, 2003, 1:04:28 PM8/22/03
to
"Janek Schleicher" <bi...@kamelfreund.de> wrote in message news:<pan.2003.08.22....@kamelfreund.de>...

> Michael Roper wrote at Fri, 22 Aug 2003 01:54:20 -0700:
>
> > Is it possible to store pieces of an expression in a variable that will get
> > expanded before the larger expression is evaluated? For example, could I

> perldoc -f eval


> my $result = eval "$x $op $y";

> eval STRING is one of the things that you usually won't use unless you
> really want to use it :-)

eval is not evil, only use it with caution.

If something wrong happens running the code, the variable $@ will have
the error string.

my $result = eval "$x $op $y";

if ($@) {
# something was wrong ...
warn $@; # will show the error
# you can do something about it.
}

Michael Roper

unread,
Aug 22, 2003, 1:54:15 PM8/22/03
to
Janek Schleicher writes:
> May I ask what the problem is you need to evaluate operators for.

Sure, maybe that's for the best. :) I have tried eval and can't get
anything to work. I need to modify a script that represents my one and only
foray into Perl, it was done a year ago, and I'm sure there's a better way.
(Also, any corrections to tortured terminology, concepts or usage on my part
is appreciated. Whatever I learned a year ago is now fuzzy at best.)

I'm starting with an ugly log generated by my server-side spam blocker. I
use one script to nicely format that log and strip out any entry that is not
a description of a blocked email. The result is a complete list of all
blocked emails, with each record formatted as (in a fixed-width font):

-----------------------------------------------------------------
Date: 04.06.2003 07:42:43

IP: 12.255.39.76
Sender: de59...@mail.com
Recipient: mich...@encraft.com

Response: 550 5.2.1 Mailbox unavailable.
Reason: SPAMCOP [Spam Source, Various Others -- 127.0.0.2]
Details: Blocked - see http://spamcop.net/bl.shtml?12.255.39.76

Blacklist: www.spamcop.net
Lookup: www.spamcop.net/w3m?action=checkblock&ip=12.255.39.76
-----------------------------------------------------------------

Because of the volume of blocked emails that need to be checked (for
collateral damage), I wrote a second script that further strips out log
records that are, for whatever reason, guaranteed to be spam and don't need
to be manually confirmed.

It's the second script I'm having trouble with. One of the main goals in
writing it was to make it easy to manually add, modify, or delete (within
the script itself) the "definitely spam" descriptions used to cull records
from the log. For that, I use:

my @aDelete =
({
Name => 'open relay',
Marker=> '^ Reason: (NJABL|OSIRUSOFT) \[Open Relay',
Count => "0",
},{
Name => 'china and korea',
Marker=> ' (china|korea) does not seem to care about spam$',
Count => "0",
});

When I process the log, I check each log entry ($sLogRecord) against each
Marker. If it's found, I increment the Count for the Marker. When I'm
done, I use the Name and Count for each Marker to display a summary table of
the results, as well as any log records that survived deletion (and
therefore need to be checked as possible collateral damage):

foreach $sLogRecord ( @aLogRecords )
{

#---------------------------------------------------------------------------
-----------------#
# compare against marker of each record type to be deleted

#---------------------------------------------------------------------------
-----------------#
for( $i = 0; $i <= $#aDelete; $i++ )
{
if( $sLogRecord =~ /$aDelete[$i]->{Marker}/m )
{
$aDelete[$i]->{Count}++;
$sLogRecord = undef;
}
}
}

This may be ugly, but it has worked well and I've been able to easily modify
the entries in @aDelete as needed. The problem I have now is that for the
first time I'd like to delete a record if it doesn't match a particular
Marker. So, I'd like to add an entry to @aDelete such as:

{
Name => 'invalid recipient',
Marker=> '^Recipient: michaelr@encraft\.com)$',
Count => "0",
}

and then rather than:

if( $sLogRecord =~ /$aDelete[$i]->{Marker}/m )

do this instead:

if( $sLogRecord !~ /$aDelete[$i]->{Marker}/m )

It seemed to me that the cleanest way to do this was to include the desired
operator in the @aDelete entry itself:

my @aDelete =
({
Name => 'invalid recipient',
Marker=> '^Recipient: michaelr@encraft\.com)$',
Op => "!~",
Count => "0",
},{
Name => 'open relay',
Marker=> '^ Reason: (NJABL|OSIRUSOFT) \[Open Relay',
Op => "=~",
Count => "0",
},{
Name => 'china and korea',
Marker=> ' (china|korea) does not seem to care about spam$',
Op => "=~",
Count => "0",
});

But I have been unable to find a way to then write:

if( $sLogRecord $aDelete[$i]->{Op} /$aDelete[$i]->{Marker}/m )

that will work in the intended fashion. I have tried every permutation of
eval I can think of.

I realize that I can solve this problem in other ways. It's just that the
only solutions I've come up with are pretty ugly. Any thoughts much
appreciated.

Michael Roper


Uri Guttman

unread,
Aug 22, 2003, 2:26:34 PM8/22/03
to
>>>>> "FG" == Francesc Guasch <frankier...@etsetb.upc.es> writes:

>> perldoc -f eval
>> my $result = eval "$x $op $y";
>> eval STRING is one of the things that you usually won't use unless you
>> really want to use it :-)

FG> eval is not evil, only use it with caution.

no, only use eval when is does something that is much harder (or
impossible) to do in other ways. caution is too light a word especially
when newbies are asking about eval. almost all eval uses by newbies can
be done with better and safer with hashes, code refs, dispatch tables,
etc.

the point is that eval is a last resort and not the first technique to
try. it is very rarely needed and used way too often. so the 'dogma'
here is to never support its use unless you have a clear understanding
of what it does, how it can be unsafe and a very strong reason why it is
the best solution.

uri

--
Uri Guttman ------ u...@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org

Michael P. Broida

unread,
Aug 22, 2003, 4:12:14 PM8/22/03
to

Combine that with someone's prior suggestion involving
using functions for the operators. Put the FUNCTION NAME
in your "Op" entry and call that function to perform the
operation needed.

Mike

Uri Guttman

unread,
Aug 22, 2003, 5:17:02 PM8/22/03
to
>>>>> "MPB" == Michael P Broida <michael....@boeing.com> writes:

please edit the quoted post.

<snip of full quote>

MPB> Combine that with someone's prior suggestion involving
MPB> using functions for the operators. Put the FUNCTION NAME
MPB> in your "Op" entry and call that function to perform the
MPB> operation needed.

and that is getting back to symrefs which are bad.

Tad McClellan

unread,
Aug 22, 2003, 2:46:00 PM8/22/03
to
Uri Guttman <u...@stemsystems.com> wrote:
>>>>>> "FG" == Francesc Guasch <frankier...@etsetb.upc.es> writes:
>

> >> my $result = eval "$x $op $y";

> FG> eval is not evil, only use it with caution.


>
> no, only use eval when is does something that is much harder (or
> impossible) to do in other ways. caution is too light a word especially
> when newbies are asking about eval. almost all eval uses by newbies can
> be done with better and safer with hashes, code refs, dispatch tables,
> etc.


For those of you following along at home, you should note that
what is being discussed here is the "eval EXPR" form.

"eval BLOCK" is not evil.


--
Tad McClellan SGML consulting
ta...@augustmail.com Perl programming
Fort Worth, Texas

Eric Schwartz

unread,
Aug 22, 2003, 5:55:42 PM8/22/03
to
Uri Guttman <u...@stemsystems.com> writes:
>>>>>> "MPB" == Michael P Broida <michael....@boeing.com> writes:
> MPB> Combine that with someone's prior suggestion involving
> MPB> using functions for the operators. Put the FUNCTION NAME
> MPB> in your "Op" entry and call that function to perform the
> MPB> operation needed.
>
> and that is getting back to symrefs which are bad.

A way of doing the same thing that doesn't require symrefs is to use
a hash of subrefs:

my %funcHs = ( '+' => sub { $_[0] + $_[1] },
'-' => sub { $_[0] - $_[1] },
'*' => sub { $_[0] * $_[1] },
'/' => sub { $_[0] / $_[1] } );

Then call it like so:

my $op = '+';
print $funcHs{$op}->(1, 2), "\n";

-=Eric
--
Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
-- Blair Houghton.

Uri Guttman

unread,
Aug 22, 2003, 8:43:34 PM8/22/03
to
>>>>> "ES" == Eric Schwartz <emsc...@pobox.com> writes:

ES> Uri Guttman <u...@stemsystems.com> writes:
>>>>>>> "MPB" == Michael P Broida <michael....@boeing.com> writes:
MPB> Combine that with someone's prior suggestion involving
MPB> using functions for the operators. Put the FUNCTION NAME
MPB> in your "Op" entry and call that function to perform the
MPB> operation needed.
>>
>> and that is getting back to symrefs which are bad.

ES> A way of doing the same thing that doesn't require symrefs is to use
ES> a hash of subrefs:

ES> my %funcHs = ( '+' => sub { $_[0] + $_[1] },
ES> '-' => sub { $_[0] - $_[1] },
ES> '*' => sub { $_[0] * $_[1] },
ES> '/' => sub { $_[0] / $_[1] } );

full circle. did you even see sam holden's post with the same solution?
the reason i posted was to stop the thread from going back to
symrefs. the code ref solution was shown and discussed already.

James Willmore

unread,
Aug 23, 2003, 2:38:15 AM8/23/03
to
On Fri, 22 Aug 2003 13:46:00 -0500
ta...@augustmail.com (Tad McClellan) wrote:
> For those of you following along at home, you should note that
> what is being discussed here is the "eval EXPR" form.
>
> "eval BLOCK" is not evil.

We now return to ... "The Eval Perl Programmers Do" ... already in
progress.

--
Jim

Janek Schleicher

unread,
Aug 24, 2003, 2:55:54 AM8/24/03
to

Here's another way to get it:

my @aDelete = (
{Name => 'invalid recipient',

Marker => qr/^Recipient: michaelr@encraft\.com$/,
HasToMatch => 0


},
{Name => 'open relay',

Marker => qr/^ Reason: (NJABL|OSIRUSOF) \[Open Relay/,
HasToMatch => 1


},
{Name => 'china and korea',

Marker => qr/ (china|korea) does not seem to care about spam$/,
HasToMatch => 1
}
);

if ($sLogRecord =~ /$aDelete[$i]->{Marker}/m == $aDelete[$i]->{HasToMatch})
{
$aDelete[$i]->{Counter}++;
}

That works because the regexp returns the number of successfull matches in
your string what is in your case 0 or 1, so it must be equal to the
HasToMach entry.

(I also used the qr// operator for the regexep parts what might be a bit
better, please read perldoc perlop [Regexp Quote-Like Operators] about)


This way has some advantages over the eval-method.
First it generates more and more sensful errors if something goes wrong.
E.g. if you type in the Op part only a = instead of =~, there is nor a
runtime error and I believe neither a warning.
I would find it also a bit easier to work in other contexts as you don't
need to distinguish between to operators but between two different
selfexplaining boolean states.
It is also a bit shorter as you don't need four characters ('=~') but only
1 to achieve the same in every entry of @aDelete.

However the main advantage is the better logic. Whether a Marker has to
match or not is basically not a question of which operator has to be used,
it's primary a question of "yes" or "no". Thus a boolean variable is more
appropriate to the problem.

(That would it also make it easier to port your program to other
languages, e.g. Perl 6, sometimes)


Greetings,
Janek

0 new messages