Tails has asked for the wisdom of the Perl Monks concerning the following question:

Hello! I am working on a program to familiarize myself with testing modules and subroutines. I have everything working properly except for checking if the inputted value is a numeric value or not, and if it isn't, I need to return "Undef". I was given a black box subroutine to do the work for me, but I don't know how I'd implement it into my program >:. I'll post my whole code, and can anyone show me how to do my checking for whether or not an inpytted value is numeric or not? Here's my code, and beneath the module are the tests I use.
sub Average { my @num = @_; #declare variables my $avg; my $check = @num; if (!@num) { #If there is no inputted value, return 0 return 0; } elsif (AsNumber($_[0]) ){ #foreach list, get the average foreach(@num){ $avg += $_ for @_; return ($avg / scalar @_ ); } } else{ return undef; } } sub Median { my $median; #declare variables my @array = @_; my @array = sort { $a <=> $b } @array; #sort to arrange the n +umbers in order if (!@_) {#If there is no inputted value, return 0 return 0; } elsif (@array % 2){ #if divisible by 2, do this to get median $median = $array[($#array / 2)]; return $median; } else{ #if not, use this to get median $median = $array[int($#array / 2)] + (($array[int($#array / 2) ++ 1] - $array[int($#array / 2)]) / 2); return $median; } } sub Low { my @list = @_; #declare variables and assign values my $check = @list; if (!@list) { return 0;#If there is no inputted value, return 0 } else { @list = sort {$a <=> $b} @list; #sort and find lowest value my $low = $list[0]; #take lowest value and return the value return $low; } } sub High { my @array = @_; my $array = @_; if (!@array) { #If there is no inputted value, return 0 return 0; } elsif (looks_like_number(@array)){ @array = sort { $b <=> $a } @array; #reverse sort to find the +highest number my $high = $array[0]; #grab the highest value and return return $high; } else{ return undef; } } # Utility Subroutines ... # AsNumber( $s ) returns # the number encoded by $s, or # undef if $s is not defined, or # undef if $s is not a number sub AsNumber { my ($s) = @_; return undef unless defined( $s ); my $number; eval { # make conversion failure a fatal error use warnings FATAL => qw(numeric); # try to use $s as a number $number = 0 + $s; }; if ($@) { # exception happened, thus not a number $number = undef; } return $number; } 1; # module end, must return true }
END MODULE -> BEGIN TEST
use strict; use warnings; use diagnostics; use Scalar::Util qw(looks_like_number); use Test::More 'no_plan'; use ListStats; print "========================================\n"; diag( "Tests of Average" ); #AVERAGE IS DONE YEEEEEEEEAH # fix/add more tests for Average ... is( Average( ), 0, "Average empty list" ); is( Average( (2) ), 2, "Average singleton list" ); is( Average( (-7..5) ), -1, "Average list" ); is( Average( ("a") ), undef, "Average non-number in list" ); is( Average( 1, 2, 3, 4), 2.5, "Average numeric string - test o +ne" ); is( Average( 52, "53", 93, 2678), 719, "Average numeric string +- test two" ); print "========================================\n"; diag( "Tests of Median" ); is( Median( ), 0, "Median empty list" ); is( Median( (2) ), 2, "Median singleton list" ); is( Median( (-7..5) ), -1, "Median list" ); is( Median( ("a") ), undef, "Median non-number in list" ); is( Median( 0, "1", 2 ), 1, "Median numeric string - test one" ) +; is( Median( 0, "65", 34 ), 34, "Median numeric string - test two +" ); #find length of string, get middle. print "========================================\n"; diag( "Tests of Low" ); # fix/add more tests for Low ... is( Low( ), 0, "Low empty list" ); is( Low( (2) ), 2, "Low singleton list" ); is( Low( (0,2,-2,-1) ), -2, "Low list" ); is( Low( ("a") ), undef, "Low non-number in list" ); is( Low( 0, "-1", 2 ), -1, "Low numeric string - test one" ); is( Low( 34, "-1", -47 ), -47, "Low numeric string - test two" + ); print "========================================\n"; diag( "Tests of High" ); is( High( ), 0, "High empty list" ); is( High( (2) ), 2, "High singleton list" ); is( High( (0,2,-2,-1) ), 2, "High list" ); is( High( ("a") ), undef, "High non-number in list" ); is( High( 0, "-1", 2 ), 2, "High numeric string - test one" ); is( High( 0, "-1", 2,47, 2737 ), 2737, "High numeric string - +test two" );
As you can see I'm pretty new to perl =P. @_ will be tests I input through the main perl file. The above code is part of my module. I need to make it so if @_ is not a numeric value, it returns undef, and not "a" as that is what is expected in my test file. Any help would be extremely appreciated! Thanks in advance! Also, any tips to make my program more "elegant" would be welcome too ^^ Thanks!

Replies are listed 'Best First'.
Re: How to implement numeric checking in my program.
by kcott (Archbishop) on Nov 16, 2010 at 01:44 UTC

    Take a look at the looks_like_number() function in Scalar::Util. The module is distributed with Perl so there's no installation required.

    Your High() routine would appear to have some problems. The condition elsif(1) will always be TRUE so you'll never get to the call to AsNumber(). Once you've fixed that up, note that you have no terminal else - what happens if the three (els)if conditions are all FALSE?

    -- Ken

Re: How to implement numeric checking in my program.
by ikegami (Patriarch) on Nov 16, 2010 at 01:37 UTC
    @_ can't be a numeric value. It's an array. I think you want to check that each element of @_ is a numeric value. So check each element of @_ in turn. If you find one that isn't numeric, return undef. Otherwise, proceed with your calculations.

    PS - Sorting to find the highest value doesn't scale as well as simply checking each value in turn to see if it's higher than the highest one seen to date.

    PPS - Your prof could have used Scalar::Util's looks_like_number rather than using fatal warnings and eval. His solution doesn't even work in all circumstances. (Specifically, objects that numify to undef.)

Re: How to implement numeric checking in my program.
by PeterPeiGuo (Hermit) on Nov 16, 2010 at 01:39 UTC

    The simplest way is to copy and paste the sub into your program, although a better approach would be wrapping this in a class or module etc. And you just need to call the sub as showed below (observe the result from the print out, you will see some missing lines when the input is not numeric.):

    sub AsNumber { my ($s) = @_; return undef unless defined( $s ); my $number; eval { # make conversion failure a fatal error use warnings FATAL => qw(numeric); # try to use $s as a number $number = 0 + $s; }; if ($@) { # exception happened, thus not a number $number = undef; } return $number; } print AsNumber(123), "\n"; print AsNumber("123"), "\n"; print AsNumber("abc"), "\n"; print AsNumber(1.2e-10), "\n"; print AsNumber(1.23), "\n"; print AsNumber("1.2a"), "\n";

    Peter (Guo) Pei

Re: How to implement numeric checking in my program.
by JavaFan (Canon) on Nov 16, 2010 at 08:19 UTC
    As a side-note, in general, don't use $@ to test whether an eval succeeded or failed. It's quite easy to come up with examples where an eval failed, but $@ is false, or where an eval succeeded, but $@ is true (for instance, due to actions happening at DESTROY time). Always make sure your block returns true, and test the return value of eval: if it's undefined, the eval failed:
    eval { # make conversion failure a fatal error use warnings FATAL => qw(numeric); # try to use $s as a number $number = 0 + $s; 1; } or do { my $err = $@ || "Unknown error"; # exception happened, thus not a number $number = undef; };
Re: How to implement numeric checking in my program.
by aquarium (Curate) on Nov 16, 2010 at 02:30 UTC
    test that input number +0e equals (numerically) input.
    the hardest line to type correctly is: stty erase ^H