Can somebody explain why my loop does not work?
#!/usr/bin/perl -w
use warnings;
use strict;
use URI;
use Web::Scraper;
my $url = "http://example.com";
# prepare data
my $scrapedata = scraper {
process "div.something", 'pages[]' => '#rel';
};
# scrape the data
my $res = $scrapedata->scrape(URI->new($url));
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
my $varpages = $res->{pages}[$j];
print "$varpages\n";
}
for ( my $count = 2; $count <= $varpages; $count++) {
print "$varpages\n";
print "$count\n";
}
This is the error :
# perl oli
Global symbol "$varpages" requires explicit package name at oli line 25.
Global symbol "$varpages" requires explicit package name at oli line 27.
Execution of oli aborted due to compilation errors.
$varpages is lexically scoped to the code block:
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
my $varpages = $res->{pages}[$j];
print "$varpages\n";
}
The attempt to reference it later references a different variable. If you want $varpages to have global scope, declare it outside the block that assigns to it. For example:
my $varpages;
# Get number of pages and define as var
for my $j (0 .. $#{$res->{pages}}) {
$varpages = $res->{pages}[$j];
print "$varpages\n";
}
Related
Using the following code as a reference...
#!/usr/bin/perl
use strict;
use warnings;
my $counter = 0;
until ($counter == 10){
$counter++;
}
print $counter;
..by doing this, the $counter variable is accessible outside the until loop, thus running this will print "10".
However, it's been nagging me.. How can I declare the $counter varible, in adherence with 'use warnings' (and possibly strict?), but only have it accesible within the until loop.
Whilst I'm not expert.. having coded in perl for a few years now, instinctively the following should work...
#!/usr/bin/perl
use strict;
use warnings;
until (my $counter == 10){
$counter++;
}
but it seems that by not first declaring $counter as a numerical value, and because we're using a == in the expression, the following warning is printed..
Use of uninitialized value $counter in numeric eq (==) at test.pl line 5.
I know it might seem anal. But I must satisfy the hunch I'm getting..!
Use a C-style for loop:
for ( my $counter = 0; $counter != 10; $counter++ ) {
...;
}
Or better yet, a counting loop:
for my $counter ( 0 .. 9 ) {
...;
}
I have tested the following code with perl 5.8 (codepad) and perl 5.16 . There's probably some deeper principle I'm missing and I'm curious what the logic behind this behavior is. Thanks.
The following simple recursive subroutine reference
use strict;
use warnings;
my $fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * $fact->($n-1);
};
print $fact->(100);
results in the error
Global symbol "$fact" requires explicit package name at line 9.
Execution aborted due to compilation errors.
declaring variable before defining it does not produce this error.
use strict;
use warnings;
my $fact;
$fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * $fact->($n-1);
};
print $fact->(100);
my returns the new lexical, so it can be used to assign to it in the same statement in which it was declared. However, any further references to that name only resolve to that lexical beginning with the next statement.
So separate the declaration:
my $fact;
$fact = sub { ... $fact ... }
This rule is in fact sometimes useful; you can have an outer lexical and an inner one and assign between the two:
my $foo = 42;
{
my $foo = $foo;
$foo += 42;
print "foo is $foo\n";
}
print "foo is $foo\n";
If you have a recent version of Perl, you don't actually need to access $fact within the subroutine because the __SUB__ pseudo-constant offers a reference to the current subroutine.
use 5.016;
my $fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * __SUB__->($n-1);
};
Note that in the first example (where the $fact variable is used within the sub), a reference cycle is created, which might lead to Perl leaking memory over time. __SUB__ is a fairly clean way to resolve that issue. (Other solutions to the problem include the Y-combinator and reference weakening.)
Please look at the following piece of code:
srand(localtime);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
I invoke the above piece of code multiple times with sufficient time(5-10 sec) in between, still the output sequence is the same.
As I have set the seed to localtime every invocation must use a different seed and perhaps generate a different sequence of three numbers, because of the time gap. Why do I get the same sequence again and again.
Note: The code is NOT in a loop, it is in a Perl file which is being executed multiple times.
The documentation says that this seed fails if multiple instances run in the same 'second' leading to the same seed - here it is not the case.
EDIT:: The solution by #simbabque does help but the randomness expected is not gained. Look at my comment on the aforementioned solution below.
Try running this with use strict and use warnings. It will give you:
Argument "Thu Jun 21 13:04:41 2012" isn't numeric in srand at ...
And right there is your problem. localtime returns a string in scalar context. Try using time instead, which returns the unix timestamp as an integer. srand needs a numerical value to work.
If you add a Data::Dumper to it you'll see that the seed with your code is always 1.
no strict; no warnings;
use Data::Dumper;
print Dumper srand(localtime);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
Says:
$VAR1 = 1;
0
2
6
What you need is:
use strict; use warnings;
srand(time);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
Edit:
This still is not a very good idea if you want good randomness. The doc says:
In versions of Perl prior to 5.004 the default seed was just the
current time. This isn't a particularly good seed, so many old
programs supply their own seed value (often time ^ $$ or time ^ ($$ +
($$ << 15)) ), but that isn't necessary any more.
I suggest you just omit the call to srand at all unless you actually want reproducable results (i.e. for testing).
In general, there is no reason to expect better randomness by repeatedly seeding a PRNG.
You can use the following script to check what's going on with your original question:
#!/usr/bin/env perl
use strict; use warnings;
use 5.014;
for (1 .. 3) {
my $seq = newseq(3, 5);
printf "Seed = %s\n", $seq->{seed};
my $it = $seq->{generator};
while (defined(my $r = $it->())) {
print "$r\n";
}
sleep 5;
}
sub newseq {
my ($length, $limit) = #_;
$length //= 10;
$limit //= 10;
my $seed = srand(time);
return {
seed => $seed,
generator => sub {
return unless $length-- > 0;
return rand($limit);
},
};
}
However, if you do need statistically independent generators, you can use Math::Random::MT::Auto and create individual PRNG objects:
#!/usr/bin/env perl
use strict; use warnings;
use 5.014;
use strict;
use warnings;
use Math::Random::MT::Auto qw(:!auto);
my $prng1 = Math::Random::MT::Auto->new(SOURCE => '/dev/random');
my $prng2 = Math::Random::MT::Auto->new(SOURCE => 'random_org');
say $prng1->rand();
say $prng2->irand();
I have the following question:
I want to create a perl script that reads from a text file (file with several columns of numbers) and calculate some statistics (mean, median, sd, variance). I already built one script, but as I am not in love yet with perl, I can't fix the problems of syntax on it...
Here is my perl script..
#!/usr/bin/perl -w
use strict;
open(FILEHANDLE, data.txt);
while (<FILEHANDLE>) {
shift #ARGV;
my #array = split(\t,$_);
}
close(FILEHANDLE);
###### mean, sum and size
$N = $sum = 0;
$array[$x-1];
$N++;
$sum += $array[$x-1];
###### minimum and the maximum
($min = 0, $max = 0);
$max = $array[$x-1] if ($max < $array[$x-1]), (my#sorted = sort { $a <=> $b } #samples) {
print join(" ",#sorted);
}
##### median
if ($N % 2==1) {
print "$median = $sorted[int($N/2)]\n"; ## check this out
};
else ($median = ($sorted[$N/2] + $sorted[($N/2)-1]) / 2)) {
print "$median\n"; # check this out
};
##### quantiles 1º and 3º
if $qt1 = $sorted[$r25-1] {
print "\n"; # check this out
};
else $qt1 = $fr*($sorted[$ir] - $sorted[$ir-1]) + $sorted[$ir-1] {
print "\n"; # check this out
};
##### variance
for (my $i=0;
$i<scalar(#samples);
$i++)
{
$Var += ($samples[$i]-$mean)**2;
$Var = $Var/($N-1);
};
###### standard error
($Std = sqrt($Var)/ sqrt($N));
############################################################
print "$min\n";
print "$max\n";
print "$mean\n";
print "$median\n";
print "$qt1\n";
print "$var\n";
print "$std\n";
exit(0);
I want to get it working. Please help. THANKS IN ADVANCE!
Errors in your code:
open(FILEHANDLE, data.txt);
data.txt needs to be quoted. You are not checking the return value of the open, e.g. ... or die $!. You should use a lexical filehandle and three argument open, e.g. open my $fh, '<', "data.txt" or die $!.
shift #ARGV;
This does nothing except remove the first value from you argument list, which is then promptly discarded.
my #array = split(\t,$_);
You are using \t as a bareword, it should be a regex, /\t/. Your #array is declared inside a lexical scope of the while loop, and will be undefined outside this block.
$N = $sum = 0;
Both variables are not declared, which will cause the script to die when you use strict (which is a very good idea). Use my $N to solve that. Also, $N is not a very good variable name.
$array[$x-1];
This will do nothing. $x is not declared (see above), and also undefined. The whole statement does nothing, it is like having a line 3;. I believe you will get an error such as Useless use of variable in void context.
$N++;
This increments $N to 1, which is a useless thing to do, since you only a few lines above initialized it to 0.
Well.. the list goes on. I suggest you start smaller, use strict and warnings since they are very good tools, and work out the errors one by one. A very good idea would be to make subroutines of your calculations, e.g.:
sub sum {
# code here
return $sum;
}
Go to perldoc.perl.org and read the documentation. Especially useful would be the syntax related ones and perlfunc.
Also, you should be aware that this functionality can be found in modules, which you can find at CPAN.
Your main problem is you have not declared your variables such as $N, $max, etc.
You need to introduce all new variables with my the first time you reference them. Just like you did with $array and $i. So for example
$N = $sum = 0;
Should become
my( $N, $sum ) = ( 0, 0 );
the following code
#!/usr/bin/env perl
use strict;
use warnings;
my #foo = (0,1,2,3,4);
foreach my $i (#foo) {
sub printer {
my $blah = shift #_;
print "$blah-$i\n";
}
printer("test");
}
does not do what I would expect.
What exactly is happening?
(I would expect it to print out "test-0\ntest-1\ntest-2\ntest-3\ntest-4\n")
The problem is that the sub name {...} construct can not be nested like that in a for loop.
The reason is because sub name {...} really means BEGIN {*name = sub {...}} and begin blocks are executed as soon as they are parsed. So the compilation and variable binding of the subroutine happens at compile time, before the for loop ever gets a chance to run.
What you want to do is to create an anonymous subroutine, which will bind its variables at runtime:
#!/usr/bin/env perl
use strict;
use warnings;
my #foo = (0,1,2,3,4);
foreach my $i (#foo) {
my $printer = sub {
my $blah = shift #_;
print "$blah-$i\n";
};
$printer->("test");
}
which prints
test-0
test-1
test-2
test-3
test-4
Presumably in your real use case, these closures will be loaded into an array or hash so that they can be accessed later.
You can still use bareword identifiers with closures, but you need to do a little extra work to make sure the names are visible at compile time:
BEGIN {
for my $color (qw(red blue green)) {
no strict 'refs';
*$color = sub {"<font color='$color'>#_</font>"}
}
}
print "Throw the ", red 'ball'; # "Throw the <font color='red'>ball</font>"
Eric Strom's answer is correct, and probably what you wanted to see, but doesn't go into the details of the binding.
A brief note about lexical lifespan: lexicals are created at compile time and are actually available even before their scope is entered, as this example shows:
my $i;
BEGIN { $i = 42 }
print $i;
Thereafter, when they go out of scope, they become unavailable until the next time they are in scope:
print i();
{
my $i;
BEGIN { $i = 42 }
# in the scope of `my $i`, but doesn't actually
# refer to $i, so not a closure over it:
sub i { eval '$i' }
}
print i();
In your code, the closure is bound to the initial lexical $i at compile time.
However, foreach loops are a little odd; while the my $i actually creates a lexical, the foreach loop does not use it; instead it aliases it to one of the looped over values each iteration and then restores it to its original state after the loop. Your closure thus is the only thing referencing the original lexical $i.
A slight variation shows more complexity:
foreach (#foo) {
my $i = $_;
sub printer {
my $blah = shift #_;
print "$blah-$i\n";
}
printer("test");
}
Here, the original $i is created at compile time and the closure binds to that; the first iteration of the loop sets it, but the second iteration of the loop creates a new $i unassociated with the closure.