How to print an array in a matrix format - perl

I am creating a cypher program. I want to transcript the key (given in $ARGV[1]) to a matrix of numbers.
But, I have some troubles figuring out how to print the array as a matrix without getting warnings/errors.
use strict;
use warnings;
use POSIX;
my #characters = split //, $ARGV[1];
#characters = map {ord($_)} 0 .. $#characters;
my $col_nb = ceil(sqrt($#characters));
for my $i (1 .. ($col_nb**2 - $#characters - 1)) { push #characters , 0; }
foreach my $i (0 .. $col_nb - 1) {
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. $col_nb * ($i + 1)];
printf("\n");
}
I am triyng to get an output like this : (key = "abcd")
48 49
50 51
But, I get these errors on the output :
Redundant argument in printf at test.perl line 9.
48 49
Redundant argument in printf at test.perl line 9.
50 51

You are off by one. Your array slice contains 3 numbers, but you only want 2. Change:
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. $col_nb * ($i + 1)];
to:
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. ($col_nb * ($i + 1) - 1)];
You can add use diagnostics; to get a more verbose warning message:
(W redundant) You called a function with more arguments than other
arguments you supplied indicated would be needed. Currently only
emitted when a printf-type format required fewer arguments than were
supplied, but might be used in the future for e.g. "pack" in perlfunc.
Only use printf when you are doing formatting. Change:
printf("\n");
to:
print "\n";

Check the edge cases and off-by-one errors.
Also, you want the square root of the number of characters, not the number - 1.
Moreover, you don't need to create another array to hold the numbers, you can map the characters to them on the fly when printing.
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw{ ceil };
my #characters = split //, $ARGV[1];
my $col_nb = ceil(sqrt #characters);
for my $i (0 .. $col_nb - 1) {
printf "%d\t" x $col_nb,
map defined ? ord : 0,
#characters[$col_nb * $i .. $col_nb * ($i + 1) - 1];
print "\n";
}

Related

Scoping in Perl

As a biology student, I'm trying to extend my programming knowledge and I ran into a problem with Perl.
I'm trying to create a program that generates random DNA strings and performs analysis work on the generated data.
In the first part of the program, I am able to print out the strings stored in the array, but the second part I cannot retrieve all but one of the elements of the array.
Could this be part of the scoping rules of Perl?
#!usr/bin/perl
# generate a random DNA strings and print it to file specified by the user.
$largearray[0] = 0;
print "How many nucleotides for the string?\n";
$n = <>;
$mylong = $n;
print "how many strings?\n";
$numstrings = <>;
# #largearray =();
$j = 0;
while ( $j < $numstrings ) {
$numstring = ''; # start with the empty string;
$dnastring = '';
$i = 0;
while ( $i < $n ) {
$numstring = int( rand( 4 ) ) . $numstring; # generate a new random integer
# between 0 and 3, and concatenate
# it with the existing $numstring,
# assigning the result to $numstring.
$i++; # increase the value of $i by one.
}
$dnastring = $numstring;
$dnastring =~ tr/0123/actg/; # translate the numbers to DNA characters.
#print $dnastring;
#print "\n";
$largearray[j] = $dnastring; #append generated string to end of array
#print $largearray[j];
#print $j;
#IN HERE THERE ARE GOOD ARRAY VALUES
#print "\n";
$j++;
}
# ii will be used to continuously take the next couple of strings from largearray
# for LCS matching.
$mytotal = 0;
$ii = 0;
while ( $ii < $numstrings ) {
$line = $largearray[ii];
print $largearray[ii]; #CANNOT RETRIEVE ARRAY VALUES
print "\n";
$ii++;
#string1 = split( //, $line );
$line = $largearray[ii];
#print $largearray[ii];
#print "\n";
$ii++;
chomp $line;
#string2 = split( //, $line );
$n = #string1; #assigning a list to a scalar just assigns the
#number of elements in the list to the scalar.
$m = #string2;
$v = 1;
$Cm = 0;
$Im = 0;
$V[0][0] = 0; # Assign the 0,0 entry of the V matrix
for ( $i = 1; $i <= $n; $i++ ) { # Assign the column 0 values and print
# String 1 See section 5.2 of Johnson
# for loops
$V[$i][0] = -$Im * $i;
}
for ( $j = 1; $j <= $m; $j++ ) { # Assign the row 0 values and print String 2
$V[0][$j] = -$Im * $j;
}
for ( $i = 1; $i <= $n; $i++ ) { # follow the recurrences to fill in the V matrix.
for ( $j = 1; $j <= $m; $j++ ) {
# print OUT "$string1[$i-1], $string2[$j-1]\n"; # This is here for debugging purposes.
if ( $string1[ $i - 1 ] eq $string2[ $j - 1 ] ) {
$t = 1 * $v;
}
else {
$t = -1 * $Cm;
}
$max = $V[ $i - 1 ][ $j - 1 ] + $t;
# print OUT "For $i, $j, t is $t \n"; # Another debugging line.
if ( $max < $V[$i][ $j - 1 ] - 1 * $Im ) {
$max = $V[$i][ $j - 1 ] - 1 * $Im;
}
if ( $V[ $i - 1 ][$j] - 1 * $Im > $max ) {
$max = $V[ $i - 1 ][$j] - 1 * $Im;
}
$V[$i][$j] = $max;
}
} #outer for loop
print $V[$n][$m];
$mytotal += $V[$n][$m]; # append current result to the grand total
print "\n";
} # end while loop
print "the average LCS value for length ", $mylong, " strings is: ";
print $mytotal/ $numstrings;
This isn't a scoping issue. You have declared none of your variables, which has the effect of implicitly making them all global and accessible everywhere in your code
I reformatted your Perl program so that I could read it, and then added this to the top of your program
use strict;
use warnings 'all';
which are essential in every Perl program you write
Then I added
no strict 'vars';
which is a very bad idea, and lets you get away without declaring any variables
The result is this
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 60.
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 61.
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 67.
Argument "j" isn't numeric in array element at E:\Perl\source\dna.pl line 42.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 60.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 61.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 67.
Bareword "j" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 42.
Execution of E:\Perl\source\dna.pl aborted due to compilation errors.
Line 42 (of my reformatted version) is
$largearray[j] = $dnastring
and lines 60, 61 and 67 are
$line = $largearray[ii];
print $largearray[ii]; #CANNOT RETRIEVE ARRAY VALUES
and
$line = $largearray[ii];
You are using j and ii as array indexes. Those are Perl subroutine calls, not variables. Adding use strict would have stopped this from compiling unless you had also declared sub ii and sub j
You might get away with it if you just change j and ii to $j and $ii, but you are certain to get into further problems
Please make the same changes to your own code, and declare every variable that you need using my as close as possible to the first place they are used
You should also improve your variable naming. Things like #largearray are pointless: the # says that it's an array, and whether it's large or not is relative, and of little use in understanding your code. If you have no better description of its purpose then #table or #data are probably a little better
Likewise, please avoid capital letters and most single-letter names. #V, $Cm and $Im are meaningless, and you would need fewer comments if those names were better
You certainly wouldn't need comments like # end while loop and # outer for loop if you had indented your blocks properly and kept them short enough so that both the beginning and the end can be seen on the screen at the same time, and the fewer comments you can get away with the better, because they badly clutter the code structure
Finally, it's worth noting that the C-style for loop is rarely the best choice in Perl. Your
for ( $i = 1; $i <= $n; $i++ ) { ... }
is much clearer as
for my $i ( 1 .. $n ) { ... }
and declaring the control variable at that point makes it unnecessary to invent new names like $ii for each new loop
I think you have a typo in your code:
ii => must be $ii
don't forget to put this at the beginning of your code:
use strict;
use warnings;
in order to avoid this (and others) kind of errors

Splitting and tallying substrings within mixed integer-string data

Input Data (example):
40A3B35A3C
30A5B28A2C2B
Desired output (per-line) is a single number determined by the composition of the code 40A3B35A3C and the following rules:
if A - add the proceeding number to the running total
if B - add the proceeding number to the running total
if C - subtract the proceeding number from the running total
40A 3B 35A 3C would thus produce 40 + 3 + 35 - 3 = 75.
Output from both lines:
75
63
Is there an efficient way to achieve this for a particular column (such as $F[2]) in a tab-delimited .txt file using a one-liner? I have considered splitting the entire code into individual characters, then performing if statement checks to detect A/B/C, but my Perl knowledge is limited and I am unsure how to go about this.
When you use split with a capture, the captured group is returned from split, too.
perl -lane '
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * ("C" eq $op ? -1 : 1) while ($n, $op) = splice #ar, 0, 2;
print $s
' < input
Or maybe more declarative:
BEGIN { %one = ( A => 1,
B => 1,
C => -1 ) }
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * $one{$op} while ($n, $op) = splice #ar, 0, 2;
print $s
When working through a string like this, it's useful to know that regular expressions can return a list of results.
E.g.
my #matches = $str =~ m/(\d+[A-C])/g; #will catch repeated instances
So you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
while (<DATA>) {
my $total;
#break the string into digit+letter groups.
for (m/(\d+[A-C])/g) {
#separate out this group into num and code.
my ( $num, $code ) = m/(\d+)([A-C])/;
print "\t",$num, " => ", $code, "\n";
if ( $code eq "C" ) {
$total -= $num;
}
else {
$total += $num;
}
}
print $total, " => ", $_;
}
__DATA__
40A3B35A3C
30A5B28A2C2B
perl -lne 'push #a,/([\d]+)[AB]/g;
push #b,/([\d]+)[C]/g;
$sum+=$_ for(#a);$sum-=$_ for(#b);
print $sum;#a=#b=();undef $sum' Your_file
how it works
use the command line arg as the input
set the hash "%op" to the
operations per letter
substitute the letters for operators in the
input evaluate the substituted input as an expression
use strict;
use warnings;
my %op=qw(A + B + C -);
$ARGV[0] =~ s/(\d+)(A|B|C)/$op{$2} $1/g;
print eval($ARGV[0]);

Using big numbers in Perl

I have a scenario where I take 2 very big binary strings (having 100 characters) and I need to add them.
The issue is that I am getting the answer in the form 2.000xxxxxxxxxxe+2, whereas I need the precise answer, as another 100 character long string.
chomp($str1=<STDIN>);
chomp($str2=<STDIN>);
print "Str 1 is $str1\n";
print "Str 2 is $str2\n";
$t = $str1 + $str2;
print "Sum is $t\n";
Sample Input
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sample Output
Str1 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Str2 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sum is
2.0022022220202e+099
As already suggested, you can use Math::BigInt core module,
use Math::BigInt;
# chomp($str1=<STDIN>);
# chomp($str2=<STDIN>);
# print "Str 1 is $str1\n";
# print "Str 2 is $str2\n";
my $t = Math::BigInt->new("0b$str1") + Math::BigInt->new("0b$str2");
print $t->as_bin;
In order to perform arithmetic on your strings, Perl converts them to floating-point numbers, which are inherently imprecise. If you want to avoid that, use Math::BigInt as already suggested ... or roll your own.
######## WARNING/GUARANTEE: This is practically certain to be
# slower, buggier, less portable, and less secure than Math::BigInt.
# In fact, I planted a security hole just to prove a point. Enjoy.
use strict;
use warnings;
sub addition {
my ($int1, $int2) = #_;
my #int1 = reverse split //, $int1;
my #int2 = reverse split //, $int2;
my $len = scalar(#int1>#int2 ? #int1 : #int2);
my #result;
my $carry = 0;
for (my $i=0; $i < $len; ++$i)
{
$int1[$i] |= 0;
$int2[$i] |= 0;
my $sum = $carry + $int1[$i] + $int2[$i];
if ($sum >= 10)
{
$carry = int($sum / 10);
$sum %= 10;
}
push #result, $sum;
}
push #result, $carry if $carry;
return join ('', reverse #result);
}

Looking for a perl scripts to make addition and pick last number

I am trying to write a simple perl script to learn Perl. This is the first script I have written using user input. The script needs to get a last numbers after make addition function Any help would be appreciated. Below is what I have so far.
Example user input 9423 and then the scripts were make addition function like below
09+04=13
04+02=06
02+03=05
03+09=12
print "Enter 4 Digits Number";#9423
chomp($number = <STDIN>);
EDIT
How to pick a last 2 digits numbers so the results are 3652
#!/usr/bin/perl
my #nums = ("9423" =~ /(\d{1})/g);
my $a = $nums[0];
my $b = $nums[1];
my $c = $nums[2];
my $d = $nums[3];
my $ab= $a+$b;
my $bc= $b+$c;
my $cd= $c+$d;
my $da= $d+$a;
printf "%02d\n%02d\n%02d\n%02d\n", $ab, $bc, $cd, $da;
I think, here code will output your expected result
#!/usr/bin/perl
use warnings;
use strict;
print "Enter 4 Digits Number:";#9423
chomp(my $number = <STDIN>);
my #digits = split("", $number);
my #lasts;
# add first digit to the last position
#digits = 94239
$digits[$#digits + 1] = $digits[0];
for(my $i = 0; $i < $#digits; $i++){
$lasts[$i] = ($digits[$i] + $digits[$i + 1]) % 10;
}
print join "",#lasts,"\n";
output after enter number 9423:
3652
probably easiest way to do it is use a for loop and substring
EDIT (now tested):
for my $i (0 .. length($number)-2) {
print substr($number, $i, 1) + substr($number, $i+1, 1);
}
Perhaps the following will be helpful:
use strict;
use warnings;
print "Enter a 4 Digit Number: ";
chomp( my $num = <STDIN> );
my #nums = split //, $num;
$nums[ $#nums + 1 ] = $nums[0];
( $nums[$_] + $nums[ $_ + 1 ] ) =~ /(.)$/ and print $1 for 0 .. $#nums - 1;
Output after entering 9423:
3652

Print Armstrong numbers between 1 to 10 million

How to write a logic using for loop or while loop for printing Armstrong numbers?
Someone kindly explain how to print Armstrong numbers between 1 to 1,00,00,000.
This the algorithm that I followed
step 1 : initializing variable min,max,n,sum,r,t
step 2 : my $n = <>;
step 3 : to find base of $n
step 4 : using for loop
( for (n = min; n < max ; n++ )
step 5 : some logic like
n=t,sum =0,r=t%10,t=n/10,
step 6 :
sum = sum + (n ^ base );
step 6 :
if ( sum == num ) print Armstrong numbers else not.
I tried to code this my code look like this
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my $n;
chomp($n);
my $min = 1;
my $max = 10000000
my $r;
my $sum;
my $t;
my $base = length($n);
print "base is $base\n";
for ($n = $min; $n <= $max; $n++) {
$t = $n;
$sum = 0;
while ($t != 0) {
$r = $t % 10;
$t = $t / 10;
{
$sum = $sum + ($base * $r);
}
if ($sum == $n) {
print "$n\n";
}
}
}
Several things:
It's bad practice to declare something with my until you need it.
You must remember that numbers are also strings, and can be manipulated by string functions like split.
C-like loops are discouraged in Perl because they're hard to read.
Constants should be ...well... constant.
Here's my attempt. I use split to split up my digits into an array of digits. This is a lot easier than dividing constantly by ten. I can get the number of digits by simply taking the scalar value of my #digits array.
I can then loop through #digits, taking each one to the power of $power and adding it to sum. I use the map command for this loop, but I could have used another for loop too.
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
use constant {
MIN => 1,
MAX => 1_000_000,
};
for my $number ( (+MIN..+MAX) ) {
my #digits = split //, $number;
my $power = #digits;
my $sum = 0;
map { $sum += $_**$power } #digits;
if ( $sum == $number ) {
say "$number is an Armstrong number";
}
}
And my output:
1 is an Armstrong number
2 is an Armstrong number
3 is an Armstrong number
4 is an Armstrong number
5 is an Armstrong number
6 is an Armstrong number
7 is an Armstrong number
8 is an Armstrong number
9 is an Armstrong number
153 is an Armstrong number
370 is an Armstrong number
371 is an Armstrong number
407 is an Armstrong number
1634 is an Armstrong number
8208 is an Armstrong number
9474 is an Armstrong number
54748 is an Armstrong number
92727 is an Armstrong number
93084 is an Armstrong number
548834 is an Armstrong number
Took a bit over five seconds to run.
Instead of map, I could have done this loop:
for my $digit ( #digits ) {
$sum = $sum + ( $digit ** $power);
}
Did this one at university...
I dug out the one I made in C and converted it to perl for you (it may not be the best way to do this, but it is the way I did it):
#!/usr/bin/env perl
use strict;
use warnings;
my $min = 1;
my $max = 10000000;
for (my $number = $min; $number <= $max; $number++) {
my #digits = split('', $number);
my $sum = 0;
foreach my $digit (#digits) {
$sum += $digit**length($number);
}
if ($sum == $number) {
print "$number\n";
}
}
(Demo - 1 to 9999 due to execution time limit)
Your code seems to be right, but you have some kind of problems with your start. For example you dont read from STDIN or from #ARGV. Would you do that, you just have a small problem with your calculating of the exponential calculation. In most Programming Languages, the syntax for a exponential calculation is ** or a pow() function.
I really dont understand, for what this part is:
while ($t != 0) {
$r = $t % 10;
$t = $t / 10;
{
$sum = $sum + ($base * $r);
}
if ($sum == $n) {
print "$n\n";
}
}
For what is the naked block? Why do you use the modulus? .. Well i give you a small code for calculating the armstrong numbers with bases of 1..100, between 0 and 10million:
#!/usr/bin/perl
use strict;
use warnings;
foreach my $base (0..100) { # use the foreach loop as base
for my $num (0..10_000_000) { # and use numbers between this range
my $ce=0; # ce = calculated exp.
foreach my $num2 (split //,$num ) { # split each number for calc each
$ce += $num2 ** $base; # exp. and adding it (see algorithm)
}
if ($num == $ce) { # when the exp. num and the number
print "$base => $num\n"; # itself equals, its a armstrong number
} # print that
}
}