Comparing two arrays character by character perl - arrays

I have two arrays:
#Array1 which contains A,B,C,D,E
and
#Array2 which contains L,B,C,F,E
I tried using a foreach to go through every element in the arrays and say if it's true or not to get started but I get dirty big fat errors when doing anything.
The program works perfectly with one foreach, but with a nested one it bugs out:
foreach my $var (#Array1){
print "Letter $var";
foreach my $var2 (#Array2){
if($var2 eq $var) {
print "They are equal";
} else {
next;
}
}
}
Any Ideas; one error is:
Use of uninitialized value in string eq at compare.pl line 192, <> line 2.

I've run it:
use strict;
use warnings;
my #Array1=qw/A B C D E/;
my #Array2=qw/L B C F E/;
foreach my $var (#Array1){
$\=$/;
print "Letter $var";
foreach my $var2 (#Array2){
if($var2 eq $var) {
print "They are equal";
} else {
next;
}
}
}
and I've got it
Letter A
Letter B
They are equal
Letter C
They are equal
Letter D
Letter E
They are equal

Your error means one of the values in either array is undefined. Since you do not show how the arrays are assigned values, it is impossible to tell why. It is clear that the problem lies in code you do not show.
What you can do to debug the problem is to print the arrays with the Data::Dumper module:
use Data::Dumper;
print Dumper \#Array1, \#Array2;
Then you should see what values they contain.
Also:
It is quite unnecessary to use your else { next } block, since it is the last part of the loop.
Printing "They are equal" would be quite a useless statement for bigger arrays, since you would get output that states that something is equal to something else, but you would only know one of the elements.

You might also want to use array_diff(#arr1, #arr2)
of Array::Utils.

As TLP says one of the values in the array is undef.
You can change the code to something like this to solve the problem.
foreach my $var (#Array1){
print "Letter $var \n";
foreach my $var2 (#Array2){
#(Both are undef (so they match)) OR (both are defined AND MATCH)
if((!defined($var2) && !defined($var)) ||
(defined($var2) && defined( $var) && ($var2 eq $var))) {
print "They are equal\n";
} else {
next;
}
}
}

Related

How do I make the STDIN in the Array stop? (PERL) [duplicate]

This question already has answers here:
Perl script does not print <STDIN> multiple times
(2 answers)
Closed 5 years ago.
My #array will not stop taking in STDIN...
my #array = undef;
while (#array = undef){
#array = <STDIN>;
for (#array[x]=5){
#array = defined;
}
}
As clarified, limit the STDIN to five lines
use warnings;
use strict;
use feature 'say';
my #input;
while (<STDIN>) {
chomp;
push #input, $_;
last if #input == 5;
}
say for #input;
There are other things to comment on in the posted code. While a good deal of it is cleared up in detail in Dave Cross answer, I'd like to address the business of context when reading from a filehandle.
The "diamond" operator <> is context-aware. From I/O Operators (perlop)
If a <FILEHANDLE> is used in a context that is looking for a list, a list comprising all input lines is returned, one line per list element. It's easy to grow to a rather large data space this way, so use with care.
In the usual while loop the <> is in the scalar context
while (my $line = <$fh>)
and it is the same with while (<$fh>) since it assigns to $_ variable, a scalar, by default.
But if we assign to an array, say from a filehandle $fh with which a file was opened
my #lines = <$fh>;
then <> operator works in the list context. It reads all lines until it sees EOF (end-of-file), at which point it returns all lines, which are assigned to #lines. Remember that each line has its newline. You can remove them all by
chomp #lines;
since chomp works on a list as well.
With STDIN this raises an issue when input comes from keyboard, as <> waits for more input since EOF isn't coming on its own. It is usually given as Ctrl+D† on Unixy systems (Ctrl+Z on Windows).
So you can, in principle, have #array = <STDIN> and quit input with Ctrl+D but this may be a little awkward for input expected from keyboard, as it mostly implies the need for line by line processing. It is less unusual if STDIN comes from a file,
script.pl < input.txt
or a pipe on the command line
some command with output | script.pl
where we do get an EOF (courtesy of EOT).
But I'd still stick to a customary while when reading STDIN, and process it line by line.
† The Ctrl+D is how this is usually referred to but one actually types a low-case d with Ctrl. Note that Ctrl and c (labeled as Ctrl+C) does something entirely different; it sends the SIGINT signal, which terminates the whole program if not caught.
my #array = undef;
while (#array = undef){
These two lines don't do what (I assume) you think they are doing.
my #array = undef;
This defines an array with a single element which is the special value undef. I suspect that what you actually wanted was:
my #array = ();
which creates an empty array. But Perl arrays are always empty when first created, so this can be simplified to:
my #array;
The second line repeats that error and adds a new one.
while (#array = undef) {
I suspect you want to check for an empty array here and you were reaching for something that meant something like "if #array is undefined). But you missed the fact that in Perl, assignment operators (like =) are different to comparison operators (like ==). So this line assigns undef to #array rather than comparing it. You really wanted #array == undef - but that's not right either.
You need to move away from this idea of checking that an array is "defined". What you're actually interested in is whether an array is empty. And Perl has a clever trick that helps you work that out.
If you use a Perl array in a place where Perl expects to see a single (scalar) value, it gives you the number of elements in the array. So you can write code like:
my $number_of_elements = #an_array;
The boolean logic check in an if or while condition is a single scalar value. So if you want to check if an array contains any elements, you can use code like this:
if (#array) {
# #array contains data
} else {
# #array is empty
}
And to loop while an array contains elements, you can simply write:
while (#array) {
# do something
}
But here, you want to do something while your array is empty. To do that, you can either invert the while condition logic (using ! for "not"):
while (!#array) {
# do something
}
Or you can switch to using an until test (which is the opposite of while):
until (#array) {
# do something
}
I'm going to have to stop there. I hope this gives you some insight into what is wrong with your code. I'm afraid that this level of wrongness permeates the rest of your code too.

Dereferencing an array from an array of arrays in perl

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

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 to get around the scope of the foreach loop in Perl

So I'm working through a bit of code that my compiler really doesn't like. There are two arrays, that have an identical number of indexes. #array is populated with 0's, #otherarray is populated sequentially. In this foreach loop, it skips over the first value because it is filled outside the loop. Count is declared as 1 outside the loop as well.
foreach (#array) {
if ($count == 1) {
} elsif($_ == 0 && #otherarray[$count-1] != undef) {
$_ = $count;
splice(#otherarray, #otherarray[$count - 1], 1);
} else {
$_ = $otherarray[ rand #otherarray ];
}
$count++
}
It insists that I have use of uninitialized value in numeric ne(!=) on this line, and every line in which other array is inside an else/if/elsif statement:
elsif($_ == 0 && #otherarray[$count-1] != undef)
How do I work around this? I'm sure it's obvious but I'm really new to Perl, so I'm probably setting something up wrong in the first place? I have already declared my #otherarray.
It is the undef in the comparison which is uninitialized. Use defined instead of comparing to undef:
elsif($_ == 0 && defined($otherarray[$count-1]))

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

Resources