PDA

View Full Version : problems passing value to subroutine


shadkeene
08-23-2007, 05:18 PM
Hi All,
Back again for some advice from some very smart folks. I'm pulling weather data from the web and manipulating it. I'm using airport codes for various weather data, and I use the same subroutine multiple times for various airports. I've had some success passing various airport codes to the other subroutines, but in this one it's not working. The actual subroutine works by itself when I add the airport identifier and take out the Sub { header. But when I use it as a subroutine and pass an airport identifier into the web address...which should bring up the data for that airport...it returns nothing...no error message either. I'm fairly new to using lwp::useragent, so it may be that I'm using it as a subroutine incorrectly.

Here's the code if you can lend some help...thanks!
#!/perl/bin/perl -w
use CGI qw(:standard);
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
use strict;
use DBI;
use LWP::Simple;
use HTML::TokeParser::Simple;
my $SLPdata=();





print header;
print start_html("Gradients");






my $SLP = FcstPress("sfo");
print "$SLP";

sub FcstPress {
my $url = 'http://68.226.77.253/text/NAM80km/NAM_k@_.txt';
use LWP::UserAgent;
my $browser = LWP::UserAgent->new();
my $res = $browser->get($url) or die "Error getting file: $!";
my @lines = split('\n' ,$res->content);
foreach (@lines) {
if ($_ =~ m/^ Mean/i) {
my @SLPdata = split(' ',$_);
print $SLPdata[5];
}
}
}
print end_html;

KevinADC
08-23-2007, 09:39 PM
where would "sfo" get used in the FcstPress subroutine? You never imported "sfo" into the subroutine:

sub FcstPress {
my $airport = shift;

now use $airport where needed in the subroutine.

shadkeene
08-23-2007, 09:57 PM
Not quite following Kevin...
I thought that when I called the subroutine using the following:
my $SLP = FcstPress("sfo");
print "$SLP";
it would place "sfo" in where the "@_" is in the web address and then proceed to extract the data for this web address:
http://68.226.77.253/text/NAM80km/NAM_ksfo.txt

I used the same pattern for other data I'm extracting with lwp::simple...here's the code:

my $SF= Pressure("ksfo"); #call subroutine for SLP and define Onshore and Offshore Gradients
print "The San Fran SLP is $SF<br>";
my $SAC= Pressure("ksac");
print "The Sacramento SLP is $SAC<br>";
my $SFOSAC1=$SF-$SAC;
my $SFOSAC = sprintf("%1.1f", $SFOSAC1);
print "The Onshore Gradient is $SFOSAC<br>";




my $ACV= Pressure("acv");
print "The Arcata SLP is $ACV<br>";
my $SFOACV1=$SF-$ACV;
my $SFOACV = sprintf("%1.1f", $SFOACV1);
print "The Offshore Gradient is $SFOACV<br>";




my $STS= Pressure("sts");
print "The Santa Rosa SLP is $STS<br>";
my $SFOSTS1=$SF-$STS;
my $SFOSTS = sprintf("%1.1f", $SFOSTS1);
print "The near-offshore gradient is $SFOSTS<br>";

sub Pressure
{

my @content = getstore("http://www.atmos.albany.edu/cgi/sa-cgi?@_[0]", "press.html");
die "couldn't get it!" unless defined @content;


my $page = "press.html";


open(FH, "$page") or die $!;
my @file = <FH>;
close FH;


my $file= join "", @file;

my $p = HTML::TokeParser::Simple->new( \$file );

while ( my $token = $p->get_token ) {
next unless $token->is_text;
my $line = $token->as_is;
$SFOpdata=$line;
}
my @SFO = split(/\s+/, $SFOpdata);

use List::Util qw(first);
my $element = first { /\d{4}/ } @SFO;
}

So for each time I call the Pressure subroutine for a different airport code, it plugs in the code at the end of the @content web address and goes through and extracts/parses pressure data. Please tell me if I'm completely missing something. Thanks,
Shad

FishMonger
08-23-2007, 10:02 PM
Was the typo in the $url assignment intentional?
Should it be: http://68.226.77.253/text/NAM80km/NAM_KOMA.txt

FishMonger
08-23-2007, 10:07 PM
Are you sure KFSO is a valid station ICAO? When you use KFSO in the search form ( http://www.wxcaster.com/models_text.htm ), it returns a "page cannot be found" error.

shadkeene
08-23-2007, 11:45 PM
Fishmonger...thanks for validating...it should be ksfo if I made a type (san francisco intl airport).

KevinADC
08-24-2007, 06:07 AM
Not quite following Kevin...
I thought that when I called the subroutine using the following:
my $SLP = FcstPress("sfo");
print "$SLP";
it would place "sfo" in where the "@_" is in the web address and then proceed to extract the data for this web address:
http://68.226.77.253/text/NAM80km/NAM_ksfo.txt


Look at the code you posted previoulsy:

sub FcstPress {
my $url = 'http://68.226.77.253/text/NAM80km/NAM_k@_.txt';

$url is a single-quoted string. the @_ symbol has no meta meaning in a single-quoted string. It would in a double-quoted string but that is a poor habit to get into using an array in a string, especially the system array: @_. You should almost always import the parameters you send to a sub routine into a list or array or a scalar declared locally to the sub routine using "my":

sub FcstPress {
my $var = shift || return "Error: No argument sent to FcstPress";
my $url = "http://68.226.77.253/text/NAM80km/NAM_k$var.txt";

shadkeene
08-24-2007, 07:02 AM
Thanks Kevin...just to clarify, how would I still call the subroutine in the main portion of the code?
Would it be more like?:

FcstPress($sfo,$acv,$sac);

But I'm not connecting where the shift function and $var are tied together? I think I'm following that your point is to go through the array of airport identifiers one-by-one using the shift function...each time going to the next airport and returning the data for that airport. But how do I call that in the main code and once called, how do I declare variables that hold onto that data the subroutine just extracted? Hopefully my questions make sense. Thanks very much for your time.
Shad

KevinADC
08-24-2007, 08:33 AM
If you pass a list to the sub routine then you declare a list or an array when you import it in the sub routine:

FcstPress($sfo,$acv,$sac);

sub FcstPress {
my @identifiers = @_; #<--imported to a named array
foreach my $airports (@indentifiers) {
do something with $airport
}
}

'shift' is an array operator that returns the first element of an array, removing it from the array. When you send an argument to a subroutine you can use:

my $var = shift;

and perl will return the first element of the system array, it's the same as:

my $var = shift @_;

and almost the same as

my $var = $_[0];

except $_[0] does not remove the first element from the array, it only assigns it's value to the scalar on the left.

same with:

my ($var1,$var2,$var3) = @_;

putting parenthesis around ($var1,$var2,$var3) creates a list of scalars (not to be confused with an array) with the same corresponding values in the array, but it does not change @_. Which is the same as:

my ($var1,$var2,$var3) = @_[0,1,2];

Which is an array slice.

These are subtle yet important differences to be aware of. "shift" is "destructive", it changes an array, the other ways are not "destructive" they do not change an array.

shadkeene
08-24-2007, 05:59 PM
Great...thanks very much! I'll have a good time learning and applying those principles today.
Shad

shadkeene
08-25-2007, 01:23 AM
I've gone through the examples and looked up a few items about subroutines...my code using the variable instead of system array in the double-quoted web address works, but it only returns the first value...for sfo...even though I'm trying to retrieve the value for $sac because I'm calling the variable $z. I've also tried a foreach loop in there, but not getting results from that. Thanks for any help Here's the code I have (just shows declaration of variables and subroutine call/subroutine):

my $sfo="sfo";
my $acv="acv";
my $sac="sac";


my ($x, $y, $z) = FcstPress2($sfo,$acv,$sac);
print "$z";



sub FcstPress2 {
my $apt = shift @_ || return "Error: No argument sent to FcstPress2"; #go through each argument in subroutine call and extract data
my $url = "http://68.226.77.253/text/NAM80km/NAM_k$apt.txt";
my $browser = LWP::UserAgent->new();
my $res = $browser->get($url) or die "Error getting file: $!";
my @lines = split('\n' ,$res->content);
foreach (@lines) {
if ($_ =~ m/^ Mean/i) {
my @SLPdata = split(' ',$_);
print $SLPdata[5];
}
}
}

FishMonger
08-25-2007, 02:28 AM
Try this:
use LWP::Simple;

sub FcstPress2 {
my @apt = @_ || return "Error: No argument sent to FcstPress2"; #go through each argument in subroutine call and extract data
my @data;

foreach my $icao (@apt) {
my $url = "http://68.226.77.253/text/NAM80km/NAM_k$icao.txt";
my $content = get($url) or die "Error getting file: $!";
my @lines = split('\n' ,$content);
foreach my $line (@lines) {
if ($line =~ m/^ Mean/i) {
my $SLPdata = (split(' ', $line))[5];
push @data, $SLPdata;
}
}
}
return $data[0], $data[1], $data[2]; # I've assumed that you always want to return 3 vars
}

FishMonger
08-25-2007, 02:49 AM
Change:my @apt = @_ || return "Error: No argument sent to FcstPress2"; #go through each argument in subroutine call and extract data

To: return "Error: No argument sent to FcstPress2" unless @_;
my @apt = @_;

shadkeene
08-25-2007, 03:04 AM
It works! Thanks a lot...now I think I'll be able to get some good stuff done and I've learned quite a bit from you folks. Now I've gotta ask, when you put the [5] at the end of the line in
my $SLPdata = (split(' ', $line))[5];
So that's splitting the $line up into individual elements and only keeping the
6th element and placing that in the variable? Pretty useful code. I had no clue! Tks again,
Shad

KevinADC
08-25-2007, 11:14 AM
Fish,

why did you suggest the changes you did above?

FishMonger
08-25-2007, 04:21 PM
Which changes are wondering about? Is it the error handling on the @apt assignment?

In the first case, @apt would end up being assigned the length of @_ instead of the values.

KevinADC
08-26-2007, 12:59 AM
I meant these changes:

Change:

my @apt = @_ || return "Error: No argument sent to FcstPress2"; #go through each argument in subroutine call and extract data

To:

return "Error: No argument sent to FcstPress2" unless @_;
my @apt = @_;