Dereferencing an array from an array of arrays in perl - arrays

I have various subroutines that give me arrays of arrays. I have tested them separately and somehow when i write my main routine, I fail to make the program recognize my arrays. I know it's a problem of dereferencing, or at least i suspect it heavily.
The code is a bit long but I'll try to explain it:
my #leaderboard=#arrarraa; #an array of arrays
my $parentmass=$spect[$#spect]; #scalar
while (scalar #leaderboard>0) {
for my $i(0..(scalar #leaderboard-1)) {
my $curref=$leaderboard[$i]; #the program says here that there is an uninitialized value. But I start with a list of 18 elements.
my #currentarray=#$curref; #then i try to dereference the array
my $w=sumaarray (#currentarray);
if ($w==$parentmass) {
if (defined $Leader[0]) {
my $sc1=score (#currentarray);
my $sc2=score (#Leader);
if ($sc1>$sc2) {
#Leader=#currentarray;
}
}
else {#Leader=#currentarray;}
}
elsif ($w>$parentmass) {splice #leaderboard,$i,1;} #here i delete the element if it doesn't work. I hope it's done correctly.
}
my $leadref= cut (#leaderboard); #here i take the first 10 scores of the AoAs
#leaderboard = #$leadref;
my $leaderef=expand (#leaderboard); #then i expand the AoAs by one term
#leaderboard= #$leaderef; #and i should end with a completely different list to work with in the while loop
}
So I don't know how to dereference the AoAs correctly. The output of the program says:
"Use of uninitialized value $curref in concatenation (.) or string at C:\Algorithms\22cyclic\cyclospectrumsub.pl line 183.
Can't use an undefined value as an ARRAY reference at C:\Algorithms\22cyclic\cyclospectrumsub.pl line 184."
I would appreciate enormously any insight or recommendation.

The problem is with the splice that modifies the list while it is being processed. By using the 0..(scalar #leaderboard-1) you set up the range of elements to process at the beginning, but when some elements are removed by the splice, the list ends up shorter than that and once $i runs off the end of the modified list you get undefined references.
A quick fix would be to use
for (my $i = 0; $i < #leaderboard; $i++)
although that's neither very idiomatic nor efficient.
Note that doing something like $i < #leaderboard or #leaderboard-1 already provides scalar context for the array variable, so you don't need the scalar() call, it does nothing here.
I'd probably use something like
my #result;
while(my $elem = shift #leaderboard) {
...
if ($w==$parentmass) {
# do more stuff
push #result, $elem;
}
}
So instead of deleting from the original list, all elements would be taken off the original and only the successful (by whatever criterion) ones included in the result.

There seem to be two things going on here
You're removing all arrays from #leaderboard whose sumaarray is greater than $parentmass
You're putting in #Leader the array with the highest score of all the arrays in #leaderboard whose sumaarray is equal to $parentmass
I'm unclear whether that's correct. You don't seem to handle the case where sumaarray is less than $parentmass at all. But that can be written very simply by using grep together with the max_by function from the List::UtilsBy module
use List::UtilsBy 'max_by';
my $parentmass = $spect[-1];
my #leaderboard = grep { sumaarray(#$_) <= $parentmass } #arrarraa;
my $leader = max_by { score(#$_) }
grep { sumaarray(#$_) == $parentmass }
#leaderboard;
I'm sure this could be made a lot neater if I understood the intention of your algorithm; especially how those elements with a sumarray of less that $parentmass

Related

How to I get a string from an array element found with .select without brackets in ruby?

Right now I'm having major difficulty finding a way to get a string from an array element without brackets.
I'm using .select to find a specific element in the array of strings I'm using, but when I try to print the variable I store the result to, it ends up also storing the brackets as well. I've tried numerous things, such as using .to_s and .join(''), but unforunately
found=file_arr.select {|str| str=~/\A#{find_x} #{y}/}
if visited.include?("#{found}") == false
#Do this
end
What I want to get is
#String_here
But what I'm getting instead is
[\"#String_here\n\"]
Enumerable#select will return an array containing all elements of enum for which the given block returns a true value.
Enumerable#find will return the first for which block is not false.
So in your case, you can use find instead:
found = file_arr.find { |str| str =~ /\A#{find_x} #{y}/ }
Notice your condition can be more understandable with unless:
do_something unless visited.include?("#{found}")

Creating a random generated array with a fixed number of elements, but still less than a fixed amount

newbie here, as the title says I'm trying to generate an array that has to contain random numbers (including negative ones), but no more than 19.
I came up with this:
use strict;
use warnings;
use feature qw/say/;
my #rand=();
my $all_the_numbers=int(rand(19));
my $i=0;
while ($all_the_numbers<20)
{
my $number=20-int(rand(30)); #in order to randomize negative numbers too
push #rand, $number;
$i++;
last if $i=$all_the_numbers;
}
say "#rand";
But I always end up with an array that has only one element. Any idea why?
UPDATE
First of all, thanks for helping me with the previous problem.
Now I'm trying to delete every positive number in the array, making it contain only the negative ones; the whole program looks like this:
#!/usr/bin/perl
use strict;
use warnings;
use feature qw/say/;
use 5.10.1;
my #rand=();
my $all_the_numbers=int(rand(19));
my $i=0;
while ($all_the_numbers<20)
{
my $number=20-int(rand(30));
push #rand, $number;
$i++;
last if $i==$all_the_numbers;
}
say "#rand";
my $h=0;
while ($all_the_numbers<20)
{
for (#rand)
{
if ($rand[0]<0)
{
next; #in order to make it skip the negative ones
}
shift #rand;
}
$h++;
last if $h==$all_the_numbers;
}
say "#rand";
However, this resoults in deleting only the positive numbers that come before the first negative one in the array, leaving what comes right after untouched...
So, how is it that the while loop doesn't keep on deleting the positives?
Here's a picture to better explain myself: https://i.stack.imgur.com/qi7sf.png
Thanks in advance.
Try this:
last if $i==$all_the_numbers;
Observe the operator ==.
You probably knew it already, but for the benefit of other readers that might visit:
The operator == compares for equality.
The operator = assigns the value on its right to the variable on its left.

How to determine the length of a variable in an array in Perl

I am trying to print to the terminal in all upper-case any word that at is least five characters long. My code is:
if (substr(#vdata, length(#vdata)-5, 5)) {
print "#vdata";
}
It does not seem to be working. What am I doing wrong?
Assuming that #vdata contains your list of words:
my #upper = map {length $_ > 5 ? uc $_ : ()} #vdata;
print "#upper";
You are making simple things complicated. Try something like:
print uc $_ if length $_ > 5
Let's start by making you snippet into a complete, runnable program.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my #vdata = qw[this arrays has strings of different lengths];
if (substr(#vdata, length(#vdata)-5, 5)) {
print "#vdata";
}
I've put some data into #vdata, but also (and probably more importantly) I've turned on strict and warnings. You should get into the habit of doing that for all of your code.
So what happens if we try to run this program?
$ ./vdata
length() used on #vdata (did you mean "scalar(#vdata)"?) at ./vdata line 8.
So there's your first problem. You're using the length function on an array. I get the idea that you're not thinking particularly clearly about what you're thinking. Your description of the problem seems rather vague.
I am trying to print to the terminal in all upper-case any word that at least five characters long.
I assume that you are storing the words in the array #vdata. So instead of getting the length of the array, you want to get the length of each individual element of the array. Let's start by setting up a loop to look at each element in the array.
foreach my $word (#vdata) {
# Do something with $word
}
The code within the block is run once for each element in #vdata. And each time it is executed, the element is in $word. So let's print $word if it's five characters or more.
foreach my $word (#vdata) {
say $word if length($word) >= 5;
}
(Actually, we've used say, not print, to get an automatic newline character.)
Running that prints:
arrays
strings
different
lengths
So we're on the right lines.
Now you want to print these words in upper case. That's easy, we just use the uc function.
foreach my $word (#vdata) {
say uc $word if length($word) >= 5;
}
And now the program shouts at us.
ARRAYS
STRINGS
DIFFERENT
LENGTHS
I think perhaps you were trying to do too much in one go. Programming is far easier if you break your problem down into smaller chunks and work on solving one smaller problem at a time.

How do I create an array of hashes and loop through them in Perl?

I'm trying to create an array of hashes, but I'm having trouble looping through the array. I have tried this code, but it does not work:
for ($i = 0; $i<#pattern; $i++){
while(($k, $v)= each $pattern[$i]){
debug(" $k: $v");
}
}
First, why aren't you useing strict and warnings? The following lines should be at the top of every Perl program you create, right after #!/usr/bin/perl. Always.
use strict;
use warnings;
And I know you aren't because I'm pretty sure you'd get some nice error messages out of strict and warnings from this, and from many other places in your code as well, judging by your variable use.
Second, why aren't you doing this:
for my $i (#pattern) {
..
}
That loops through every element in #pattern, assigning them to $i one at a time. Then, in your loop, when you want a particular element, just use $i. Changes to $i will be reflected in #pattern, and when the loop exits, $i will fall out of scope, essentially cleaning up after itself.
Third, for the love of Larry Wall, please declare your variables with my to localize them. It's really not that hard, and it makes you a better person, I promise.
Fourth, and last, your array stores references to hashes, not hashes. If they stored hashes, your code would be wrong because hashes start with %, not $. As it is, references (of any kind) are scalar values, and thus start with $. So we need to dereference them to get hashes:
for my $i (#pattern) {
while(my($k, $v) = each %{$i}) {
debug(" $k: $v");
}
}
Or, your way:
for (my $i = 0; $i<#pattern; $i++) { # added a my() for good measure
while(my($k, $v) = each %{$pattern[$i]}) {
debug(" $k: $v");
}
}
Try this instead:
for my $hashref (#pattern) {
for my $key (keys %$hashref) {
debug "$key: $hashref->{$key}";
}
}
The biggest problem with what you were trying was each $pattern[$i]. The each function expects a hash to work on, but $pattern[$i] returns a hashref (i.e. a reference to a hash). You could fix your code by dereferencing $pattern[$i] as a hash:
while(my($k, $v) = each %{$pattern[$i]}) {
Also, beware of the each function, it can leave the hash iterator in an incomplete state.
See the documentation for the perl data structures cookbook:
perldoc perldsc

problem with array elements in awk not being stored

I've been using awk to process hourly weather data, storing 10 arrays with as much as 8784 data elements. If an array is incomplete, i.e., stops at 8250, etc., after the "END" command I fill the remaining array elements with the last available value for the array. However, when I then print out the complete arrays, I get 0's for the filled values instead. What's causing this?? Does awk have a limit in the array size that's preventing it from filling the arrays? Following is a snippet of the awk program. In the two print statements, the first time the array elements are filled, but the second time they're empty.
Any help is appreciated because this problem is holding up my work.
Joe Huang
END{
if (lastpresstime < tothrs)
{
diffhr = tothrs - lastpresstime
for (i=lastpresstime+1;i<=tothrs+1;i++)
{
xpressinter[i]=diffhr
xpressrecords[i]=diffhr
xipress[i]=lastpress
xpressflag[i]="R"
printf("PRS xipress[%4d] =%6.1f\n",i,xipress[i]) > "ncdcfm3.prs"
printf(" xipress[%4d] =%6.1f%1s\n",i,xipress[i],xpressflag[i])
}
for (i=1;i<=tothrs+1;i++) printf("PRS xipress[%4d] =%6.1f\n",i,xipress[i])
}
~
I don't have the rep to edit your post, but here's the formatted code:
END {
if (lastpresstime < tothrs) {
diffhr = tothrs - lastpresstime
for (i=lastpresstime+1;i<=tothrs+1;i++) {
xpressinter[i]=diffhr
xpressrecords[i]=diffhr
xipress[i]=lastpress
xpressflag[i]="R"
printf("PRS xipress[%4d] =%6.1f\n",i,xipress[i]) > "ncdcfm3.prs"
printf(" xipress[%4d] =%6.1f%1s\n",i,xipress[i],xpressflag[i])
}
for (i=1;i<=tothrs+1;i++)
printf("PRS xipress[%4d] =%6.1f\n",i,xipress[i])
}
}
Note that I added a matching brace at the end.
I don't see any inherent problems in the code, so like jhartelt, I have to ask - are all of the variables properly defined? We can't tell from this sample how lastpresstime, tothrs, and lastpress get their values. In particular, if lastpress isn't, you'll get exactly the behavior you described. Note that if you have misspelled it, it will be an undefined variable and therefore use the default value of 0.
With respect to William Pursell's comment, there should also be no difference in the output of xipress[i] between the three printfs (for lastpresstime<i).
As 0 is the default value for an unknown/unused numerical variable, I would ask if you are sure, that there is no mistype in the variable names used in the END block?

Resources