הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Eval

In perl, eval can be used in two contexts - either it evaluates a block and protects agains a die (like try/catch of other languages); or takes a string and executes it as perl code.

Let's start with a simple try/catch example implemented in perl.

use strict;
use warnings;
use v5.14;
use Carp;

sub do_something {
  my ( $l1_ref ) = @_;

  if ( ref($l1_ref) ne 'ARRAY' ) {
    croak "INVALID INPUT";
  }

  foreach my  $el ( @$l1_ref ) {
    say "*" x $el;
  }
}

eval {
  do_something(10, 20, 30);
};
if ( $@ ) {
  say "Result of last eval was $@";
}

say "-- The End --";

Simple enough - but worth noting about $@. This is the eval error variable, which is set every time an eval block has failed (by dying or syntax error). Its value is set by eval to the last error message from an eval block.

This behvaior is very useful when we need to handle exceptions. Here's a short program that finds a string in files. If one of the text files in the @files array is not readable or not found, the eval block catches the die call and a warning is printed.

use strict;
use warnings;
 
sub find {
	my ($file, $term) = @_;
	open(FILE, "<$file") or die "Error opening file $file, $!\n";
 
	while(<FILE>) {
		print "$file:$_" if /$term/;
	}
}
 
my @files = qw(xyz.txt embed.pl);
my $term = 'eval';
 
for my $f (@files) {
	eval { find($f, $term) };
	print "Error in find. $@\n" if $@;
}

Using the same technique, we can create timeouts for potentially slow operations

sub timeout {
	die "TIMEOUT";
}
 
eval {
	local $SIG{'ALRM'} = \&timeout;
	alarm(5);
	my $l = <>;
	alarm(0);
};
 
print "Operation Timed out waiting for user\n" if $@ =~/TIMEOUT/;

The final example refers to regular expressions. A useful reg-ex syntax allows us to eval the replacement string as a perl code by adding a /e at the end of a substitutional regular expression.

Here's a short example that doubles every number in the line.

while(<>) {
	s/(\d+)/$1*2/eg;
	print;
}
use strict;
use warnings;
 
our $x = 3;
our $y = 5;
 
while(<>) {
	s/(\$\w+\b)/$1/eeg;
	print;
}
use strict;
use warnings;
 
my $vowels = 'aeiouy';
my $cons = 'cbdfghiklmnpqrstvwxyz';
 
our %map;
our $pat;
 
for my $class ($vowels, $cons) {
	for (split //, $class) {
		$map{$_} .= $class;
	}
}
 
for my $char (split //, shift) {
	$pat .= "[$map{$char}]";
}
 
my $re = qr/^$pat$/i;
print "REGEX is $re\n";
 
while(<>) {
	print if /$re/;
}
 
course: