A little more code handles all the common errors correctly without throwing an exception and without altering the original data.
use strict;
use warnings;
use List::Util qw(first);
use Scalar::Util qw(looks_like_number);
use Test::More tests=>5;
sub first_a_value {
my @x = @_;
my $refer = first {
exists $_->{aaa}
and looks_like_number($_->{aaa})
and $_->{aaa} < 4
} @x;
my $value = undef;
$value = $refer->{aaa} if defined $refer;
return $value;
}
my @x;
my $aaa;
@x = ( {aaa=>3} );
$aaa = first_a_value(@x);
ok ($aaa == 3, 'valid value');
@x = ( {aaa=>100} );
$aaa = first_a_value(@x);
ok (!defined($aaa), 'no numer small enough');
@x = ( {aaa=>'foo'} );
$aaa = first_a_value(@x);
ok (!defined($aaa), 'not a number');
@x = ( {bbb=>3} );
$aaa = first_a_value(@x);
ok (!defined($aaa), 'no aaa');
@x = ( {} );
$aaa = first_a_value(@x);
ok (!defined($aaa), 'no hash');
UPDATD - Added tests accidentally omitted.