operator in a variable?

M

Michael Roper

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
 
T

Ted Zlatanov

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
 
T

Ted Zlatanov

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
 
B

Brian McCauley

Ted Zlatanov said:
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);

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\\
 
S

Sam Holden

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);

$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.
 
F

Francesc Guasch

Janek Schleicher said:
Michael Roper wrote at Fri, 22 Aug 2003 01:54:20 -0700:
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.
}
 
M

Michael Roper

Janek said:
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: (e-mail address removed)
Recipient: (e-mail address removed)

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
 
U

Uri Guttman

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
 
T

Tad McClellan

Uri Guttman said:
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.
 
M

Michael P. Broida

Michael said:
Janek said:
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: (e-mail address removed)
Recipient: (e-mail address removed)

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.

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
 
E

Eric Schwartz

Uri Guttman said:
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
 
U

Uri Guttman

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.
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.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top