perl generating array based on the input pattern - arrays

Here is my program
#!/usr/bin/perl -w
use strict;
use warnings;
my $pattern = 'a .. f';
I am looking for a way to get an array from this pattern. The one way which I thought is to use split this in to three and get the array as follows. But it seems that this is not a feasible solution as in future pattern will change. It may be something like 'a..f1..9' so the split may not work there OR I have to write more generalize code for it.
So looking for a way to generate the array based on the input pattern provided.

Maybe the Parse::Range module can help you.

Try this, capture the anchor characters via regex:
my $pattern = 'a .. f';
my #ar;
if($pattern =~ /([a-f])\s*\.\.\s*([a-f])/) {
#ar = ($1..$2);
}

This needs input validation because it will probably break fairly easily on invalid patterns, but it fits your basic spec:
#!/usr/bin/env perl
use strict;
use warnings;
my $pattern = 'a .. f';
print join(' ', make_arr($pattern)), "\n";
$pattern = 'a..f1..9';
print join(' ', make_arr($pattern)), "\n";
sub make_arr {
my $pat = shift;
my #arr;
while ($pat =~ s/(\w)\s*\.\.\s*(\w)//) {
push #arr, $1 .. $2;
}
return #arr;
}
Output:
a b c d e f
a b c d e f 1 2 3 4 5 6 7 8 9

single line regex double eval:
$pattern =~ s/(\w)\s*\.\.\s*(\w)/"\"\#{['$1'..'$2']} \""/gee;
result:
a b c d e f
This will even work on:
my $pattern = 'a .. f1 .. 8';
result:
a b c d e f 1 2 3 4 5 6 7 8
To convert this string to a list is left to the reader :)
Regards
rbo

You coud use eval and change a bit your pattern :
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.1;
use Data::Dumper;
my $pattern = '"a" .. "f", 2 .. 4';
my #array = eval $pattern;
if ($#) {
say "eval failed: $#";
} else {
say Dumper \#array;
}
output:
$VAR1 = [
'a',
'b',
'c',
'd',
'e',
'f',
2,
3,
4
];

Do you realise that perl supports that sort of syntax already? i.e., 1..5 gives you the array (1,2,3,4,5). a..f gives you the array (a,b,c,d,e,f).
print join (", ", a..f), "\n" # a, b, c, d, e, f
print join (", ", a..f, 1..3), "\n" # a, b, c, d, e, f, 1, 2, 3
Actually this is basically what M42 is suggesting but he's not explicitly pointing out that perl supports syntax not unlike that. He's turning the string into the array using the eval.

Related

Referencing an element in a 2D array in Perl

I have the following code which reads in a 6x6 array from STDIN and saves it as an array of anonymous arrays. I am trying to print out each element with $arr[i][j], but the code below isn't working. It just prints out the first element over and over. How am I not accessing the element correctly?
#!/user/bin/perl
my $arr_i = 0;
my #arr = ();
while ($arr_i < 6){
my $arr_temp = <STDIN>;
my #arr_t = split / /, $arr_temp;
chomp #arr_t;
push #arr,\#arr_t;
$arr_i++;
}
foreach my $i (0..5){
foreach my $j (0..5){
print $arr[i][j] . "\n";
}
}
i and j are not the same as the variables you declared in the foreach lines. Change:
print $arr[i][j] . "\n";
to:
print $arr[$i][$j] . "\n";
warnings alerted me to this issue. You should add these lines to all your Perl code:
use warnings;
use strict;
To demonstrate the Perlish mantra that there's "more than one way to do it":
use 5.10.0; # so can use "say"
use strict;
use warnings qw(all);
sub get_data {
my ($cols, $rows) = #_;
my ($line, #rows);
my $i;
for ($i = 1; $i <= $rows and $line = <DATA>; $i++) {
chomp $line;
my $cells = [ split ' ', $line ];
die "Row $i had ", scalar(#$cells), " instead of $cols" if #$cells != $cols;
push #rows, $cells;
}
die "Not enough rows, got ", $i - 1, "\n" if $i != $rows + 1;
\#rows;
}
sub print_data {
my ($cols, $rows, $data) = #_;
for (my $i = 0; $i < $rows; $i++) {
for (my $j = 0; $j < $cols; $j++) {
say $data->[$i][$j];
}
}
}
my $data = get_data(6, 6);
print_data(6, 6, $data);
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
if we use say, that avoids unsightly print ..., "\n"
get_data is a function that can be called and/or reused, instead of just being part of the main script
get_data knows what data-shape it expects and throws an error if it doesn't get it
[ ... ] creates an anonymous array and returns a reference to it
get_data returns an array-reference so data isn't copied
print_data is a function too
both functions use a conventional for loop instead of making lists of numbers, which in Perl 5 needs to allocate memory
There is also a two-line version of the program (with surrounding bits, and test data):
use 5.10.0; # so can use "say"
my #lines = map { [ split ' ', <DATA> ] } (1..6);
map { say join ' ', map qq{"$_"}, #$_ } #lines;
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
using map is the premier way to iterate over lists of things where you don't need to know how many you've seen (otherwise, a for loop is needed)
the adding of " around the cell contents is only to prove they've been processed. Otherwise the second line could just be: map { say join ' ', #$_ } #lines;

Using an array to index into another array

I have two arrays, let's call them #a1 and #a2. What I'm trying to do is obtain elements from #a2 using the values in #a1 as indices. My current attempt doesn't work properly.
foreach (#a1) {
print $a2[$_] . "at" . $_;
}
This only prints $_ but not $a2[$_].
I sense there is a trivial solution to this, but I just can't find it.
There is nothing wrong with the code you have. I have tested a small script and it works as expected. Asi i suggested in my comment, try using something like Data::Dumper to see whats in the arrays before the loop.
use strict;
use warnings;
use Data::Dumper;
my #a1 = (0..4);
my #a2 = ("a".."e");
print Dumper \#a1, \#a2;
foreach (#a1){
print $a2[$_]." at ".$_."\n";
}
OUTPUT
$VAR1 = [
0,
1,
2,
3,
4
];
$VAR2 = [
'a',
'b',
'c',
'd',
'e'
];
a at 0
b at 1
c at 2
d at 3
e at 4
there's no reason your code shouldn't work as long as the values of the first array are valid addresses in the second array. but if all you really want to do is just get the values and address of the second array, you could just do:
for my $i (0..$#a2) {
print "$i: $a2[$i]","\n";
}
$#a2 is the last element address of the array.

How can I print a multidimensional array in Perl?

I am trying to print a multidimensional array (matrix) in Perl passing reference to array to subroutine.
Here is my code:
sub print_matrix(\#) {
my $array = shift;
for my $i ( 0 .. $#{ $array } ) {
my $row = $array[$i];
for my $j ( 0 .. $#($row) ) {
print $array[$i][$j];
}
}
}
Borodin tells you what was wrong with your code.
Now consider this module: Data::Dumper (available on CPAN). You can use this module to print any data structure: arrayref of arrayrefs (what you called a matrix), hashref of hashrefs, arrayref of hashrefs, hashref of hashrefs, or any other combination of these structures for as many dimensions as you want. Of course, if you have too many dimensions, it could lead to a confusing output.
My point is, some time ago, I was asked in an interview how I would implement this module. I thought it was a very clever question. I had to think a little because I use the module often but never bothered to figure how it works. It is in fact very simple. Imagine in your subroutine you receive a reference but you don't actually know what kind of reference it is (scalarref, arrayref, hashref, etc.), how would you determine what it is? If you have multiple possibilities, what would you do to cover all of them? Have you thought of creating a recursive function?
So, to solve your problem quickly, if you just want to print your matrix for debugging purpose, use Data::Dumper. Otherwise, if you want to do something more complex and wish to cover multiple cases, try to create a recursive function.
Here's a Data::Dumper example:
my $arrayref = [
[ qw/ a b c d / ],
[ qw/ e f g h / ],
[ qw/ i j k l / ],
];
use Data::Dumper;
print Dumper $arrayref;
And here's the result you will get:
$VAR1 = [
[
'a',
'b',
'c',
'd'
],
[
'e',
'f',
'g',
'h'
],
[
'i',
'j',
'k',
'l'
]
];
Each "row" of your matrix is printed as a list of elements, separated by a comma (and a new line), inside a pair of brackets. Be careful, if you pass it an array, it will print each elements one by one, and you will lose the "dimensions". If you only have an array, you have to pass it as a reference like this:
print Dumper \#array;
I hope this helps.
Using plain print is OK when all you have are single letter entries in your matrix, but a module like Text::Table can make it much easier to produce tidy output. For example,
#!/usr/bin/env perl
use strict;
use warnings;
use Text::Table;
my #matrix = map {
[ map sprintf('%.2f', -500 + rand(1000)), 1 .. 5 ]
} 1 .. 5;
my $mat = Text::Table->new;
$mat->load(#matrix);
print $mat;
Output:
-7.73 -83.85 -351.18 21.06 320.40
174.83 238.29 91.16 361.43 213.04
446.43 -4.82 322.81 10.38 -436.62
-128.05 195.68 199.05 288.39 115.30
-251.19 -329.35 244.13 -428.25 454.64
You can print a two-dimensional Perl array very simply with something like this
use strict;
use warnings;
my #arr_2d = (
[ qw/ a b c d / ],
[ qw/ e f g h / ],
[ qw/ i j k l / ],
);
print_2d(\#arr_2d);
sub print_2d {
my ($matrix) = #_;
print "#$_\n" for #$matrix;
}
output
a b c d
e f g h
i j k l
Update
Here's a working version of your own code. You weren't using array references properly and had parentheses where there should have been braces. This version also prints a space after each element and a newline after each row.
sub print_matrix {
my $array = shift;
for my $i ( 0 .. $#{ $array } ) {
my $row = $array->[$i];
for my $j ( 0 .. $#{ $row } ) {
print $array->[$i][$j], ' ';
}
print "\n";
}
}

How to print a particular column from tabular data

I'm Trying to print columns from data by using index key value in the outer part of a foreach loop.
my #col;
foreach(<DATA>){
#x = split(' ',$_);
#xz = ($x[0],$x[1],$x[2]) ;
#print "$x[0]\n"; This is working but i'm not expect this.
push(#col,#xz);
}
print "$col[0]\n";
__DATA__
7 2 3
3 2 8
6 7 2
I expect the output is
7 3 6
How can i do it?
Always use use strict; and use warnings;!!
You have a couple of issues:
push( #col, #xz );
In this case, you're losing your information in #xz array. After this loop, you end up with a single array that looks like this:
#col = ( 7, 2, 3, 3, 2, 8, 6, 7, 2);
So, when you print:
print "$col[0]\n";
You get that zeroth element: 7.
We can preserve the structure of the data by using a reference:
#! /usr/bin/env perl
#
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
use Data::Dumper;
my #columns;
for my $data ( <DATA> ) {
my #data_list = split /\s+/, $data;
push #columns, \#data_list;
}
say Dumper \#columns;
__DATA__
7 2 3
3 2 8
6 7 2
Here you see I've included Data::Dumper to print out the structure of #columns:
$VAR1 = [
[
'7',
'2',
'3'
],
[
'3',
'2',
'8'
],
[
'6',
'7',
'2'
]
];
As you can see, each entry in the #columns array is now another array. However, printing out $columns[0] array reference isn't going to print what you want. Instead, it's going to print the zeroth array reference: 7, 2, 3, and not the zeroth element of each array reference: 7, 3, 6.
To do that, we need a subroutine that will go through #columns and print out the the zeroth entry of each of the arrays. Here I'm creating a subroutine called fetch_index that will fetch the passed index of the passed array:
#! /usr/bin/env perl
#
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
use Data::Dumper;
my #columns;
for my $data ( <DATA> ) {
my #data_list = split /\s*/, $data;
push #columns, \#data_list;
}
say join ", ", fetch_index( 0, #columns );
sub fetch_index {
my $entry = shift; #Entry you want from all arrays
my #array = #_;
my #values;
for my $array_ref ( #array ) {
push #values, ${array_ref}->[$entry];
}
return #values;
}
__DATA__
7 2 3
3 2 8
6 7 2
The subroutine merely goes through each array reference I've stored in my array, and fetched the $entry value from that array reference. I push those into my #values array and return that.
my #col;
while (<DATA>) {
push #col, (split ' ')[0];
# push #col, /(\S+)/; # split alternative
}
print "#col\n";
__DATA__
7 2 3
3 2 8
6 7 2
output
7 3 6
Once you've absorbed the information about anonymous arrays and references in the other excellent posts here, you can start to have fun. e.g. you can often get a one liner approach to work:
perl -nE 'say [split]->[1] ' col_data.txt
would loop (-n creates an implicit while(){} loop) through the data in col_data.txt, split the topic variable ($_) creating a series of anonymous arrays from each row and then print the second element, or "column" for example.
You can use the autosplit command line option (-a) to split each row into an array called #F (mnemonic: "F" for "Field"). In later versions of perl, the -a implies the implicit while loop (-n):
perl -anE 'say $F[1] ' col_data.txt
would be the equivalent of the previous command - printing the second column:
output:
2
2
7
There is a famous and short perl workalike for cut that is a more featureful variaton on this theme, and there is this Perl Monks thread.
perl -a -F' ' -ne 'print "$F[0]\n";' data.txt
here you $F[0] is field you can change it accordingly you will get the expected output
You were pretty close I think. This is what I did (edited to reflect comments from #Borodin):
use strict;
use warnings;
sub getColumn {
my ($data, $col) = #_;
my #output = map $_->[$col], #{$data};
return #output;
}
my #data;
while (<DATA>){
push(#data, [split(' ',$_)]);
}
print join(' ', getColumn(\#data, 0), "\n");
print join(' ', getColumn(\#data, 1), "\n");
print join(' ', getColumn(\#data, 2), "\n");
__DATA__
7 2 3
3 2 8
6 7 2
That subroutine getColumn should let you retrieve any arbitrary column. When I ran it with your data I got this for output:
7 3 6
2 2 7
3 8 2

Sorting Arrays in Perl?

I am getting an error while trying to sort a simple array...
The ERROR reads: "use of uninitialized value in numeric comparison (<=>) at file.pl line #"
#!/usr/bin/perl
use strict
use wardings
use Data::Dumper
my #array
my $array
$array[1]= 5
$array[2]= 2
$array[3]= 3
$array[4]= 4
$array[5]= 1
sub numerically {$a <=> $b}
my #sortedarray = sort numerically #array;
print "#sortedarray\n";
I am just trying to sort the array to get:
1 2 3 4 5
I am new at perl so this might just be something stupid, but please help me... Thanks
Arrays are indexed starting at 0. The error comes from trying to sort the array when $array[0] is undefined.
Update: Also, in perl, one would write:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #array = qw(1 2 3 4 5);
sub numerically {$a <=> $b}
my #sortedarray = sort numerically #array;
print "#sortedarray\n";
There is no point in declaring $array -- that would be a scalar. You are only working with the array #array, even though it is called with a $. Please read the perl documentation.
first of all, you need a semi-colon at the end of every statement. second, you're not using Data::Dumper, so why do you include it? You also don't need to declare the sub:
#!/usr/bin/env perl
use strict;
use warnings;
my #sorted = sort {$a <=> $b} qw (4 2 3 1 5);
print "#sorted\n";
And there we have it.
You're missing a shedload of semicolons.
It's warnings, not wardings.
Element 0 in your array is undefined.

Resources