How can I use pop to print an array backwards in Perl? - arrays

I am beginning to learn Perl, but am having an issue printing an array in reverse order with the pop command. I want to make this work with pop and not with other commands like reverse, or using techniques like reverse indices. Whenever I run this loop with the array, #anotherArray = (1, 2, 3, 4, 5); I only end up getting " 5, 4, 3" as a result. Am I doing something wrong with my logic or syntax?
for ($i = 0; $i <= $#anotherArray + 1; $i++) {
if ($i == 0)
{
print "Third way: ";
}
$temp = pop #anotherArray;
print "$temp ";
}

use strict;
use warnings;
my #array = (-1,0,1..5);
while (#array) {
my $e = pop #array;
print "$e\n";
}

The problem is that the array is shrinking while you are popping from it, so the limit for $i in the loop is getting smaller.
The best way is to use a while loop to keep popping from the array until it is empty.
I'm not sure what the "Third way" part is supposed to mean, but you can just print that outside the loop.
Like this
use strict;
use warnings;
my #anotherArray = (1, 2, 3, 4, 5);
print "Third way: ";
while (#anotherArray) {
print pop #anotherArray;
}
output
Third way: 54321

Just another way of doing it, since it is Perl anyways:
use strict;
use warnings;
my #anotherArray = ( 1 .. 5 );
print pop #anotherArray while #anotherArray;
it just does what is says:
print the popped-of-element from anotherArray, as long as there is a number of elements in anotherArray.
This way of doing loops or placing conditionals like if or unless are fine for a single line of code, but highly frown upon when it is a whole BLOCK of code.

Related

Is there a way to create variable Arrays in Perl?

So, well I am trying around again and now I am stuck.
while (<KOERGEBNIS>){
my $counter = 0;
my $curline = $_;
for (my $run = 0; $run < $arrayvalue; $run++){
if ($curline =~ m/#tidgef[$counter]/){
my $row = substr($curline, 0, 140);
push #array$counter, $row;
print "Row $. was saved in ID: #filtered[$counter]\n";
}
$counter++;
}
}
Background is that I want to save all lines beginning with the same 8 characters in the same array so I can count the lines and start working with those arrays. The only thing I could think of right now is with switch and cases but I thought I'd ask first before throwing this code to garbage.
Example:
if theres a line in a .txt like this:
50004000_xxxxxxxxxxxxxx31
50004000_xxxxxxxxxxxxxx33
60004001_xxxxxxxxxxxxxx11
60004001_xxxxxxxxxxxxxx45
I took the first 8 chars of each line and used uniq to filter duplicates and saved them in the array #tidgef, now I want to save Line1 and Line2 in #array1 or even better #array50004000 and Line4 and Line4 to #array2 or #array60004001.
I hope I explained my problem well enough! thank you guys
You're hovering dangerously close to an idea called "symbolic references" (also known as "use a variable to get a variable's name"). It's a very bad idea, for all sorts of reasons.
It's a much better idea to use this as an excuse to learn about complex data structures in Perl. It's not really clear what you want to do with this data, but this example should get you started:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper;
my %lines;
while (<DATA>) {
chomp;
my $key = substr($_, 0, 8);
push #{$lines{$key}}, $_;
}
say Dumper \%lines;
__DATA__
50004000_xxxxxxxxxxxxxx31
50004000_xxxxxxxxxxxxxx33
60004001_xxxxxxxxxxxxxx11
60004001_xxxxxxxxxxxxxx45
You should think carefully about why you want arrays called #array50004000 #array60004001. Your program could create them, but you have no way of knowing what those names are. While the code is running, unless you are stepping through it with the debugger, they may be called #x and #y for all you know. You can't even dump their contents because you have no idea what to dump
What you're looking for is a hash, specifically a hash of arrays. Unlike the symbol table, there are operators like keys, values and each that will allow you to enquire what values have been stored in a hash
Your code would look something like this. I have used the example data from your question and put it into myfile
use strict;
use warnings 'all';
my %data;
open KOERGEBNIS, '<', 'myfile' or die $!;
while ( <KOERGEBNIS> ) {
chomp;
my ($key) = split /_/;
push #{ $data{$key} }, $_;
}
for my $key ( sort keys %data ) {
my $val = $data{$key};
print $key, "\n";
print " $_\n" for #$val;
print "\n";
}
output
50004000
50004000_xxxxxxxxxxxxxx31
50004000_xxxxxxxxxxxxxx33
60004001
60004001_xxxxxxxxxxxxxx11
60004001_xxxxxxxxxxxxxx45

"Use of uninitialized value" when indexing an array

I get the following error from Perl when trying to run the code below
Use of uninitialized value within #words in concatenation (.) or string...
It references the line where I try to create an array made up of three-word sequences (the line that starts with $trigrams). Can anyone help me figure out the problem?
my %hash;
my #words;
my $word;
my #trigrams;
my $i = 0;
while (<>) {
#words = split;
foreach $word (#words) {
$hash{$word}++;
# Now trying to create the distinct six-grams in the 10-K.
$trigrams[$i] = join " ", $words[$i], $words[$i + 1], $words[$i + 2];
print "$words[$i]\n";
$i++;
}
}
All that is happening is that you are falling off the end of the array #words. You are executing the loop for each element of #words, so the value of $i goes from 0 to $#words, or the index of the final element of the array. So the line
join " ", $words[$i], $words[$i + 1], $words[$i + 2];
accesses the last element of the array $words[$i] and two elements beyond that which don't exist.
In this case, as with any loop which uses the current index of an array, it is easiest to iterate over the array indices instead of the contents. For the join to be valid you need to start at zero and stop at two elements before the end, so 0 .. $#words-2.
It is also neater to use an array slice to select the three elements for the trigram, and use the fact that interpolating an array into a string, as in "#array", will do the same as join ' ', #array. (More precisely, it does join $", #array, and $" is set to a single space by default.)
I suggest this fix. It is essential to use strict and use warnings at the start of every Perl program, and you should declare all your variables using my as late as possible.
use strict;
use warnings;
my %hash;
while (<>) {
my #words = split;
my #trigrams;
for my $i (0 .. $#words - 2) {
my $word = $words[$i];
++$hash{$word};
$trigrams[$i] = "#words[$i,$i+1,$i+2]";
print "$word\n";
}
}
Update
You may prefer this if it isn't too terse for you
use strict;
use warnings;
my %hash;
while (<>) {
my #words = split;
my #trigrams = map "#words[$_,$_+1,$_+2]", 0 .. $#words-2;
}

Perl array element manipulation

I've been trying and trying with this one, but it just doesn't seem to click.
If I have an array with let's say 6 numbers:
#a = (1,2,3,4,5,6)
How do I get every second index ( 2, 4, 6) in this case?
how do I compute the difference of every two elements, so
the output here would be:
1 1 1 (because 2-1 =1 and 4-3 =1 and so on..)
Note: don't ever use $a or $b, they're special (sort uses them) ... it's generally better to give your variables a descriptive name, name it as to what's in there rather than what type of variable it is.
for ( my $index = 0; $index < scalar( #pairs ); $index += 2 ) {
my $first = $pairs[ $index + 0 ];
my $second = $pairs[ $index + 1 ];
my $pair = $index / 2;
my $difference = $second - $first;
print "the difference of pair $pair is $difference\n";
}
I think you should post your earlier attempts. In my opinion, the best way to learn is to learn from your mistakes, not being presented a correct solution.
For this problem, I think I would use a C-style for-loop for the first part, simply because it is straightforward, and can easily be tweaked if some new requirement comes up.
The second problem can easily be solved using a regular Perl-style for-loop.
use strict;
use warnings; # always use these two pragmas
my #nums = 1..6;
my #idx;
for (my $n = 0; $n <= $#nums; $n += 2) { # loop from 0 to max index, step 2
push #idx, $n; # store number in #idx
}
print "Indexes: #idx\n";
my #diff;
for my $n (0 .. $#nums - 1) { # loop from 0 to max index minus 1
push #diff, $nums[$n + 1] - $nums[$n]; # store diff in #diff
}
print "Diff: #diff\n";
Output:
Indexes: 0 2 4
Diff: 1 1 1 1 1
Try this:
use strict;
use warnings;
my $index = 1;
my #a = (1,2,3,4,5,6);
for (#a) {
if ($index % 2 == 0) {
my $diff = $_ - $a[$index-2];
print $diff;
}
$index++;
}
You likely want to use the new List::Util pair functions.
For your first question:
use List::Util 'pairvalues';
my #seconds = pairvalues #list; # yields (2, 4, 6)
For your second question:
use List::Util 'pairmap';
my #diffs = pairmap { $b-$a } #list; # yields (1, 1, 1)
You can use map:
my #a = 1 .. 6;
print join ' ', 'Every second:', map $a[ 1 + $_ * 2 ], 0 .. $#a / 2;
print "\n";
print join ' ', 'Differences:', map $a[ 1 + $_ * 2 ] - $a[ $_ * 2 ], 0 .. $#a / 2;
print "\n";
First: Don't use variables a and b. $a and $b are special variables used in sorting. Just be a bit more descriptive of your variables (even if it's merely #my_array) and you should be fine.
You can loop through your array any which way you like. However, I prefer to use a while loop instead of the thee part for because the three part for loop is a bit misleading. It is a while loop in disguise and the promised indexing of the loop can be misleading.
#! /usr/bin/env perl
use warnings;
use strict;
use feature qw(say);
my #array = qw( 1 2 3 4 5 6 );
my $index = 1; # Remember Perl indexes start at zero!
while ( $index <= $#array ) {
say "Item is $array[$index]";
say "The difference is " . ($array[$index] - $array[$index-1]);
$index += 2;
}
You said every second element. Indexes of arrays start at 0, so you want the odd number elements. Most of the answers use map which is a very nice little command, but does an awful lot in a single line which can make it confusing for a beginner. Plus, I don't think the Perldoc on it is very clear. There should be more simple examples.
The say is a newer version of print. However say always adds a \n at the end. You should always use strict; and use warnings;. These will catch about 90% of your programming bugs.
The qw( ... ) is a quick way to make an array. Each word becomes an array element. You don't need quotes or commas.
#!/usr/bin/perl
use strict;
use warnings;
my #ar = (1, 2, 3, 4, 5, 6);
# 1. How do I get every second index ( 2, 4, 6) in this case?
my #even = map { $_ & 1 ? $ar[$_] : () } 0 .. $#ar;
# 2. how do I compute the difference of every two elements?
my (#c, #diff) = #ar;
push #diff, -1 * (shift(#c) - shift(#c)) while #c;
use Data::Dumper;
print Dumper \#even;
print Dumper \#diff;
1;
__END__
$VAR1 = [
2,
4,
6
];
$VAR1 = [
1,
1,
1
];

Emptying array inside loop while using push in PERL

I am writing a subroutine that prints an array of non redundant elements from another array.
This code is inside my subroutine.
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
Then i call my subroutine in a loop inside my main program, for the first iteration it is OK and my new table contains one occurrence of my old table.
But after that #new_table keeps elements from past iterations and the print result is false.
I tried emptying #new_table inside my subroutine like this
#new_table = ();
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
But then my #new_table becomes empty in all iterations except for the first one.
What is the problem with this and how can i fix it ?
Due to incorrect scoping, you're reusing the #new_table and %seen of previous passes. Create these just before the loop.
my #new_table;
my %seen;
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
This can be simplified to
my %seen;
my #new_table = grep { !$seen{$_}++ } #old_table;
print "#new_table\n";
You can also use
use List::MoreUtils qw( uniq );
my #new_table = uniq(#old_table);
print "#new_table\n";
You are using use strict; use warnings;, right? If not, you should be. Always.
You can try uniq from List::MoreUtils to remove redundant elements.
my #new_table = uniq(#old_table);
To quote from perldoc
uniq LIST
distinct LIST
Returns a new list by stripping duplicate values in LIST. The order of elements in the returned list is the same as in LIST. In
scalar context, returns
the number of unique elements in LIST.
my #x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 1 2 3 5 4
my $x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 5

How can I print only the even numbered lines of an array in perl?

I am new to Perl and am trying to write a script that will only print the even numbered lines of an array. I have tried multiple different methods of finding the size to use as the condition for my while loop, but I always end up getting an infinite loop of the first line without the program terminating. The array being input is a text file, input with the form "program.pl < foo.txt". Have I made a logic or syntax error?
#input = <STDIN>;
$i = $1;
$size = $#input + $1;
while ($size >= $i) {
print "$input[$i]";
$i = ($i + $2);
}
Don't call your problem with
program.pl < foo.txt
Instead, just pass 'foo.txt' as a parameter:
program.pl foo.txt
Inside your script, rely on default reading from <> and the line number variable $.:
use strict;
use warnings;
while (<>) {
next if $. % 2; # Skip odd numbers.
print;
}
Assuming you already have an array with all of your input, in your example #input, you can get all of the even index entries into another array using an Array Slice like so:
my #input_even_entries_only = #input[grep { $_ % 2 == 0 } 0..$#input];
The expression inside the square brackets evaluates to all of the even numbers between 0 and $#input.
You can then use a regular for/foreach loop to go through the resulting array:
for my $val (#input_even_entries_only) {
print "$val";
}
If you are trying to print lines of an array indexed at even numbers then, try this:
use strict;
use warnings;
my #input = <DATA>;
for(my $i=0; $i<=$#input; $i+=2) {
print $input[$i];
}
__DATA__
1
2
3
4
5
6
Output:
1
3
5
I've no idea what you are doing with the $1 and $2 variables. Did you think they were just numbers?
When you use a variable that has not been assigned a value, it is undefined, which will be converted to 0 when used in numerical context. If you do not use use warnings, this is done silently, and will be rather confusing.
Other than that, your code is not too far off. It should be something like:
use strict;
use warnings;
my #input = <>; # <> is more flexible and does the same thing
my $i = 1;
while ($i <= $#input) {
print $input[$i];
$i += 2;
}
Though of course, storing the entire file in an array is not necessary, and most often you should just loop over it instead. Like Miller has shown in his answer, which is probably the solution I would suggest. Using a for loop like JS shows is an excellent way to control the loop.

Resources