I have an array in Perl I want to print with space delimiters between each element, except every 10th element which should be newline delimited. There aren't any spaces in the elements if that matters.
I've written a function to do it with for and a counter, but I wondered if there's a better/shorter/canonical Perl way, perhaps a special join syntax or similar.
My function to illustrate:
sub PrintArrayWithNewlines
{
my $counter = 0;
my $newlineIndex = shift #_;
foreach my $item (#_)
{
++$counter;
print "$item";
if($counter == $newlineIndex)
{
$counter = 0;
print "\n";
}
else
{
print " ";
}
}
}
I like splice for a job like this:
sub PrintArrayWithNewlines {
my $n = 10;
my $delim = " ";
while (my #x = splice #_, 0, $n) {
print join($delim, #x), "\n";
}
}
You can use List::MoreUtils::natatime:
use warnings; use strict;
use List::MoreUtils qw( natatime );
my #x = (1 .. 35);
my $it = natatime 10, #x;
while ( my #v = $it->() ) {
print "#v\n"
}
Output:
C:\Temp> x
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35
If you do not want to use any external modules, you can use array slices:
use warnings; use strict;
my #x = (1 .. 95);
my $n = 10;
for my $i ( 0 .. int #x/$n ) {
no warnings 'uninitialized';
print "#x[$n * $i .. $n * ($i + 1) - 1]\n";
}
The functions by and every in my module List::Gen can solve this problem:
use List::Gen;
for (every 10 => 'a' .. 'z') {
print "#$_\n"
}
# a b c d e f g h i j
# k l m n o p q r s t
# u v w x y z
it can also be written
foreach (by 10 => 'a' .. 'z') {
print "#$_\n"
}
or using the functional form:
mapn {print "#_\n"} 10 => 'a' .. 'z'; # #_ not #$_ here
or an iterator if that's your style:
my $letters = by 10 => 'a' .. 'z';
while (my $line = $letters->next) {
print "#$line\n";
}
You can also use map with a modification to PrintArrayWithNewlines:
#!/usr/bin/perl -w
use strict;
sub PrintArrayWithNewlines
{
my #array = #_;
my $newlineIndex = 10;
foreach my $item (#array) {
++$globalCounter;
print "$item";
if ($globalCounter == $newlineIndex) {
$globalCounter = 0;
print "\n";
}
else {
print " ";
}
}
}
my $globalCounter = 0;
my #myArray = 'a' .. 'z'
map { PrintArrayWithNewlines($_) } #myArray;
print "\n";
The output would be:
$ ./test.pl
a b c d e f g h i j
k l m n o p q r s t
u v x y z
Related
I want to substitute value of variable 'c' and 'd' to variable 'a' and 'b' respectively in the example below, and this process should go on for 'n' times.
#!/usr/bin/perl
my $a = 4;
my $b = 6;
my $c = $a + $b;
my $d = $a * $b;
print "$c\n";
print "$d\n";
$a = $c;
$b = $d;
i.e. for each iteration of a loop the calculated value of 'c' and 'd' should be the new value of 'a' and 'b' respectively for 'n' times so that new values of 'c' and 'd' will be generated. I am not able to substitute the values. How can I set the condition to a loop for 'n' times? The desired output should be in the form:
c= val1 val2 val3......valn
d= val1 val2 val3......valn.
The $a and $b variables are reserved for use by the sort operator.
This will do what you want
use strict;
use warnings;
my ($aa, $bb) = (4, 6);
my $n = 5;
for (1 .. $n) {
my ($cc, $dd) = ($aa + $bb, $aa * $bb);
print "$cc\n", "$dd\n\n";
($aa, $bb) = ($cc, $dd);
}
output
10
24
34
240
274
8160
8434
2235840
2244274
18857074560
#!/usr/bin/perl
use strict;
use warnings;
use bigint;
my ( $sum, $times ) = ( 4, 6 );
my $count = 8;
my #sum;
my #times;
for ( 1 .. $count ) {
( $sum, $times ) = ( $sum + $times, $sum * $times );
push #sum, $sum;
push #times, $times;
}
print "c = #sum\n";
print "d = #times\n";
Outputs:
c = 10 34 274 8434 2244274 18859318834 42320461010388274 798134711765191824044221234
d = 24 240 8160 2235840 18857074560 42320442151069440 798134711722871363033832960 33777428948505262401578369250143488058711040
This should work:
#!/usr/bin/env perl
## Don't use $a and $b, they are special variables
## used in sort().
my $foo=4;
my $bar=6;
## The number of iterations
my $n=5;
## These arrays will hold the generated values
my (#sums, #products);
## Use a for loop
for (1 .. $n) {
my $sum=$foo+$bar;
my $product=$foo*$bar;
## save the results in an array to print later
push #sums, $sum;
push #products, $product;
$foo=$sum;
$bar=$product;
}
print "Sum=#sums\nProduct=#products\n";
#!/usr/local/bin/perl
my #a = <DATA>;
my #grep = grep { m/^a/g } #a;
my #grep2 = grep { m/^b/g } #a;
my #xx;
my #yy;
foreach (#grep) {
my $x = (split)[1], $_;
push( #xx, $x );
}
foreach (#grep2) {
my $y = (split)[1], $_;
push( #yy, $y );
}
my #mv;
my $i;
my $j;
for ( $i = 0 ; $i < #xx ; $i++ ) {
for ( $j = 0 ; $j < #yy ; $j++ ) {
my $m = $xx[$i] + $yy[$j];
push( #mv, $m );
}
}
foreach (#mv) {
if ( $_ eq "15" ) {
print "$grep2[$mv]\n";
}
}
__DATA__
a 15 c
a 13 m
a 10 c
b 2 k
b 12 m
b 13 m
b 5 v
b 5 m
b 6 h
b 15 m
b 12 v
b 21 m
b 11 q
b 9 m
b 32 w
Add the value of 'a' with 'b' resulting 36 values. But i expecting which values are 15 and that original line in the 'b' print. example 13 from 'a 13 m' and 2 from 'b 2 k' those are add the answer is 15 i expect output is 'b 2 k'.. similarly
I expect output
b 2 k
b 5 v
b 5 m
#!/usr/bin/perl
use strict;
use warnings;
my %data;
while (<DATA>) {
my ($pre, $num, $suffix) = split;
push #{ $data{$pre} }, [$num, $suffix];
}
for my $aref (#{ $data{a} }) {
for my $bref (#{ $data{b} }) {
print "b #$bref\n" if $aref->[0] + $bref->[0] == 15;
}
}
My aligned RNA sequence is
gccuucgggc
gacuucgguc
ggcuucggcc
For which I have done the following coding
open(RNAalign, $ARGV[0]) || "Can't open $ARGV[0]: $!\n";
while ($line = <RNAalign>) {
chomp ($line);
push (#line, $line);
}
#covariences=();
foreach $i (#line) {
foreach $j (#line) {
unless ($i eq $j) {
#search1=split("",$i);
#search2=split("",$j);
$k=0;
while($k<scalar(#search1)) {
if (#search1[$k] ne #search2[$k]) {
$string="";
$string="$k: #search1[$k] #search2[$k]\n";
push (#covariences, $string);
}
$k++;
}
}
}
}
This gives me, when printing:
1: c a
8: g u
1: c g
8: g c
1: a c
8: u g
1: a g
8: u c
1: g c
8: c g
1: g a
8: c u
What I want to do is to merge all the similar position while at the same time keeping any different character that they might have. As following (the characters does not have to be in that exact order):
1: c a g
8: g u c
You can create a hash of hashes (HoH) where the keys are the positions and the values are references to hashes whose keys are the characters at each position. A partial structure of your dataset would be the following:
'8' => {
'c' => 1,
'u' => 1,
'g' => 1
},
'1' => {
'c' => 1,
'a' => 1,
'g' => 1
},
'4' => {
'u' => 1
},
Here's code that produces this HoH structure:
use strict;
use warnings;
my ( %hash, $stringNum );
while (<DATA>) {
chomp;
my $i = 0;
$stringNum++;
$hash{ $i++ }{ lc $_ } = 1 for split //;
}
for my $position ( sort { $a <=> $b } keys %hash ) {
if ( keys %{ $hash{$position} } == $stringNum ) {
my #chars = keys %{ $hash{$position} };
print "$position: #chars\n";
}
}
__DATA__
gccuucgggc
gacuucgguc
ggcuucggcc
Output:
1: c a g
8: c u g
In the while loop, the number of strings is counted, and each string is split into its characters to create a HoH. In the for loop, if the number of keys (e.g., "c") equals the number of total strings, each string varies at that position, so the position and those are printed as an instance of covariance.
Hope this helps!
use strict;
use warnings;
use Data::Dumper;
my $s = "gccuucgggc
gacuucgguc
ggcuucggcc";
print "$s\n\n";
my $data = [];
my #lines = split(/\n/,$s);
chomp(#lines);
my $row=0;
my $col=0;
foreach my $line (#lines){
my #chars = split("",$line);
$col = 0;
foreach my $char (#chars){
$data->[$row]->[$col] = $char;
$col++;
}
$row++;
}
#print Dumper($data,$col,$row);
for(my $i=$col-1;$i>=0;$i--){
my $no_diff = 0;
my $result='';my $prev='';
#print "i: $i\n";
for(my $j=$row-1;$j>=0;$j--){
#print Dumper([$i,$j,$prev,$result,$data->[$j]->[$i]]);
if ($prev eq $data->[$j]->[$i]){
$no_diff++;
}
$result .= $data->[$j]->[$i];
$prev = $data->[$j]->[$i];
}
print "$i: $result\n" if !$no_diff;
}
The program below should take an array and compress it so that there are no repeated products and add up the totals, so:
A B B C D A E F
100 30 50 60 100 50 20 90
Becomes:
A 150
B 80
C 60
D 100
E 20
F 90
The code below runs and works the way I want it to:
#! C:\strawberry\perl\bin
use strict;
use warnings;
my #firstarray = qw(A B B C D A E F);
my #secondarray = qw (100 30 50 60 100 50 20 90);
my #totalarray;
my %cleanarray;
my $i;
# creates the 2d array which holds variables retrieved from a file
#totalarray = ([#firstarray],[#secondarray]);
my $count = $#{$totalarray[0]};
# prints the array for error checking
for ($i = 0; $i <= $count; $i++) {
print "\n $i) $totalarray[0][$i]\t $totalarray[1][$i]\n";
}
# fills a hash with products (key) and their related totals (value)
for ($i = 0; $i <= $count; $i++) {
$cleanarray{ $totalarray[0][$i] } = $cleanarray{$totalarray[0][$i]} + $totalarray[1][$i];
}
# prints the hash
my $x = 1;
while (my( $k, $v )= each %cleanarray) {
print "$x) Product: $k Cost: $cleanarray{$k} \n";
$x++;
}
However before printing the hash it gives me the "Use of uninitialized value in addition (+)" error" six times. Being very new to Perl (this is my first Perl program outside of a text book), can someone tell me why this is happening? It seems like I have initialized everything...
It gives me compile errors in these lines:
my #cleanarray;
It is a hash.
my %cleanarray;
And here:
$cleanarray{ $totalarray[0][$i] } = $cleanarray{$totalarray[0][$i]} + totalarray[1][$i];
You missed the sigil of totalarray. It is $totalarray[1][$i]
The undefined message it is because $cleanarray{$totalarray[0][$i]} doesn't exists. Using the shorter:
$cleanarray{ $totalarray[0][$i] } += totalarray[1][$i];
will work without warnings.
You are using cleanarray as a hash but it is declared as an array
You may find you prefer this reorganization of your program.
use strict;
use warnings;
my #firstarray = qw (A B B C D A E F);
my #secondarray = qw (100 30 50 60 100 50 20 90);
# prints the data for error checking
for my $i (0 .. $#firstarray) {
printf "%d) %s %.2f\n", $i, $firstarray[$i], $secondarray[$i];
}
print "\n";
# fills a hash with products (key) and their related totals (value)
my %cleanarray;
for my $i (0 .. $#firstarray) {
$cleanarray{ $firstarray[$i] } += $secondarray[$i];
}
# prints the hash
my $n = 1;
for my $key (sort keys %cleanarray) {
printf "%d) Product: %s Cost: %.2f\n", $n++, $key, $cleanarray{$key};
}
For example, I have an array
my #arr = qw(0 1 2 3 4);
How do I get the following combinations:
0
01
012
0123
01234
1
12
123
1234
2
23
234
3
34
4
If any, what's the name for this kind of combination (or permutation)?
Thanks like always!
Personally I find the "C style" for loop that gbacon uses often complicates code unnecessarily. And it's usually possible to replace it with the "range-style" for loop that is easier to follow.
#!/usr/bin/perl
use strict;
use warnings;
my #arr = qw(0 1 2 3 4);
my #result;
for my $i (0 .. $#arr) {
for my $j ($i .. $#arr) {
push #result => [ #arr[$i .. $j] ];
}
}
print #$_, "\n" for #result;
Use array slices:
#! /usr/bin/perl
use warnings;
use strict;
my #arr = qw(0 1 2 3 4);
my #result;
for (my $i = 0; $i < #arr; $i++) {
for (my $j = $i; $j < #arr; $j++) {
push #result => [ #arr[$i .. $j] ];
}
}
print #$_, "\n" for #result;
Output:
0
01
012
0123
01234
1
12
123
1234
2
23
234
3
34
4
Here's a way to divide up the problem into more discrete components:
use strict;
use warnings;
sub consec_subseq_leading {
# (1, 2, 3) ==> ( [1], [1, 2], [1, 2, 3] )
return map [ #_[0 .. $_] ], 0 .. $#_;
}
sub consec_subseq {
# (1, 2, 3) ==> ( F(1, 2, 3), F(2, 3), F(3) )
# where F = consec_subseq_leading
my $j = $#_;
return map consec_subseq_leading( #_[$_ .. $j] ), 0 .. $j;
}
my #cs = consec_subseq(0 .. 4);
print "#$_\n" for #cs;