How to use variables in a equation using Perl - perl

I'm trying to use possible variables in a equation with Perl.
For example:
#!/usr/bin/perl -w
$a = "yellow";
$b = "orange";
$c = "col1fl0ur";
$c = $a + $b;
print "$a + $b = $c \n";
I want to be able to state the value for each variable $a, $b, $c, then be able to say that
$a + $b = "col1fl0ur"
You may ask; whats the point? just print out col1fl0ur, butI want to be able to use many more variables as well, such as in this case:
#!/usr/bin/perl -w
###values###
$a = "yellow";
$b = "orange";
$c = "col1fl0ur";
$d = "derp";
$e = "oplo";
$f = "qwerty";
###defining the equation###
$c = $a + $b;
$d = $a + $c;
$f = $d + $c;
###Printing###
print "$a + $b = $c \n";
print "$a + $c = $d \n";
print "$d + $c = $f \n";

It would help a lot if you explained your real problem, but something like this may help.
Note that you should never use $a and $b in live code as they are reserved variable names.
use strict;
use warnings;
my ($a, $b, $c, $d, $e, $f) = qw( yellow orange col1fl0ur derp oplo qwerty );
### defining the equation ###
my %sum;
$sum{$a}{$b} = $c;
$sum{$a}{$c} = $d;
$sum{$d}{$c} = $f;
### Printing ###
for my $pair ([$a, $b], [$a, $c], [$d, $c]) {
my ($p1, $p2) = #$pair;
printf "%s + %s = %s\n", $p1, $p2, $sum{$p1}{$p2};
}
output
yellow + orange = col1fl0ur
yellow + col1fl0ur = derp
derp + col1fl0ur = qwerty
If you want $b + $a to be the same as $a + $b then you will have to say so explicitly. For example,
$sum{$a}{$b} = $c;
$sum{$b}{$a} = $c;

you may use Overload pragma..
You can create a new package as follows:
package Tst;
use overload "+" => \&myadd;
sub new {
my $class = shift;
my $value = shift;
return bless \$value => $class;
}
sub myadd {
my ($x, $y) = #_;
$x = ref($x) ? $$x : $x;
$y = ref($y) ? $$y : $y;
my $value = '';
if ($x eq 'yellow' and $y eq 'orange'){
$value = 'col1fl0ur';
}
return $value;
}
1
Then in your Main program, you can do the things you like:
use Tst;
my $a = Tst->new('yellow');
my $b = Tst->new('orange');
my $c = $a + $b;
say $c;
This prints out col1fl0ur.

Rather than assigning values to Perl variables ($a, $b, $c, etc.) you might consider creating a data structure that will suit your purposes (whatever they may be!?!). Borodin's answer takes a partial step in that direction.
This example takes that idea a bit farther: the terms in your "mathematical" system would not be linked to individual Perl variables; instead they would be components of a larger data structure.
use strict;
use warnings;
my %xs = (
a => 'yellow',
b => 'orange',
c => 'col1fl0ur',
d => 'foo',
e => 'bar',
f => 'fubb',
g => 'blub',
);
$xs{'a + b'} = $xs{c};
$xs{'a * c'} = $xs{d};
$xs{'d / c'} = $xs{f};
$xs{'a + b - d + f'} = $xs{g};
printf("%15s = %s\n", $_, $xs{$_}) for sort keys %xs;
Output:
a = yellow
a * c = foo
a + b = col1fl0ur
a + b - d + f = blub
b = orange
c = col1fl0ur
d = foo
d / c = fubb
e = bar
f = fubb
g = blub

Related

Why is incorrect value of pi output?

I'm trying to calculate pi using following code that uses Chudnovsky algorithm. When $n is increased, then number of digits after the decimal point. However when $n is small, it can calculate correct value of pi (3.141592...) is output, but when $n is increased, it calculate incorrect value of pi. Why?
#!/usr/bin/perl
use strict;
use warnings;
use GMP qw(:all);
use GMP::Mpf qw(:all);
use GMP::Mpz qw(:all);
use GMP::Mpq qw(:all);
my $n = shift; $n = $n<7?7:$n;
$n *= 8;
my $l = int($n/7) + 1;
my $fmt = '%.' . $n . 'f';
my ($p0, $q0, $t0) = (1, 1, 0);
$p0 = mpz($p0);
$q0 = mpz($q0);
$t0 = mpz($t0);
my ($p, $q, $t, $a) = (0, 0, 0, 0);
$p = mpz($p);
$q = mpz($q);
$t = mpz($t);
$t = mpz($a);
for my $loop (1 .. $l){
$p = (2*$loop -1)*(6*$loop -1)*(6*$loop -5);
$q = ($loop**3)*(640320**3)/24;
$a = (-1)**$loop + (13591409 + 545140134*$loop);
$p *= $p0;
$q *= $q0;
$t = $t0*$p + $a*$p;
$p0 = $p;
$q0 = $q;
$t0 = $t;
}
my $x = sr(640320,$n);
$q = mpf($q,$n);
$t = mpf($t,$n);
my $pi = mpf(640320,$n)*sr(mpf(640320,$n),$n)*$q;
$pi /= mpf(12,$n)*($t + mpf(13591409,$n)*$q);
print $pi . "\n";
sub sr{
my ($x,$i) = #_;
my $s = mpf(0.0,$i);
my $s0 = mpf($x,$i);
for (1 .. $n){
$s = 1/$s0;
$s *= $x;
$s0 = ($s + $s0)/mpf(2.0,$i);
}
return $s;
}

Different result in forloop?

I want to print like this
xxx
xxxxx
xxxxxxx
xxxxxxxxx
xxxxxxxxxxx
I achive this by following code
$s = "x";
$z = 5;
$m = 1;
$m = $m+2,
$z--,
$c = " " x $z,
$st = $s x $m,
print "$c$st\n",
for(1..5);
My doubt
when i used increment and decrement operator after the print function it gave the different result
Script is
$c = " " x $z,
$st = $s x $m,
print "$c$st\n",
$m = $m+2,
$z--,
for(1..5);
It result is
x
35 xxx
54 xxxxx
73 xxxxxxx
92 xxxxxxxxx
Here 3 5 7 9 are printed by the $m and 5 4 3 2 are printed by the $z.
But, i not directly print the $m and $z then why it gave $m and $z value? How it is work?
The code
$c = " " x $z,
$st = $s x $m,
print "$c$st\n",
$m = $m+2,
$z--,
for(1..5);
is parsed as:
$c = " " x $z,
$st = $s x $m,
print ("$c$st\n", $m = $m+2, $z--),
for(1..5);
You can force different parsing by using parentheses:
$c = " " x $z,
$st = $s x $m,
print ("$c$st\n"),
$m = $m+2,
$z--
for(1..5);
But I rather suggest the following:
for(1..5) {
$c = " " x $z;
$st = $s x $m;
print ("$c$st\n");
$m = $m+2;
$z--;
}
That way you are not relying on any operator precedence which might bite you. You will immediately see which statements are contained in the loop too. (I had to read your initial code thrice to finally get it)

calculating molecular weight in perl

I have a perl script but it calculate molecular weight only when sequence is given. However I want to calculate molecular weight of protein sequences which is in fasta file.
print "Enter the amino acid sequence:\n";
$a = < STDIN > ;
chomp($a);
my #a = ();
my $a = '';
$x = length($a);
print "Length of sequence is : $x";
#a = split('', $a);
$b = 0;
my %data = (
A=>71.09, R=>16.19, D=>114.11, N=>115.09,
C=>103.15, E=>129.12, Q=>128.14, G=>57.05,
H=>137.14, I=>113.16, L=>113.16, K=>128.17,
M=>131.19, F=>147.18, P=>97.12, S=>87.08,
T=>101.11, W=>186.12, Y=>163.18, V=>99.14
);
foreach $i(#a) {
$b += $data{$i};
}
$c = $b - (18 * ($x - 1));
print "\nThe molecular weight of the sequence is $c";
first of all u must tell us what format has .fasta files. As i know they looks like
>seq_ID_1 descriptions etc
ASDGDSAHSAHASDFRHGSDHSDGEWTSHSDHDSHFSDGSGASGADGHHAH
ASDSADGDASHDASHSAREWAWGDASHASGASGASGSDGASDGDSAHSHAS
SFASGDASGDSSDFDSFSDFSD
>seq_ID_2 descriptions etc
ASDGDSAHSAHASDFRHGSDHSDGEWTSHSDHDSHFSDGSGASGADGHHAH
ASDSADGDASHDASHSAREWAWGDASHASGASGASG
if we will make suggestion that your code works fine, and counts molecular weight all we need is to read fasta files, parse them and count weight by yours code. It's more easy that sounds like.
#!/usr/bin/perl
use strict;
use warnings;
use Encode;
for my $file (#ARGV) {
open my $fh, '<:encoding(UTF-8)', $file;
my $input = join q{}, <$fh>;
close $fh;
while ( $input =~ /^(>.*?)$([^>]*)/smxg ) {
my $name = $1;
my $seq = $2;
$seq =~ s/\n//smxg;
my $mass = calc_mass($seq);
print "$name has mass $mass\n";
}
}
sub calc_mass {
my $a = shift;
my #a = ();
my $x = length $a;
#a = split q{}, $a;
my $b = 0;
my %data = (
A=>71.09, R=>16.19, D=>114.11, N=>115.09,
C=>103.15, E=>129.12, Q=>128.14, G=>57.05,
H=>137.14, I=>113.16, L=>113.16, K=>128.17,
M=>131.19, F=>147.18, P=>97.12, S=>87.08,
T=>101.11, W=>186.12, Y=>163.18, V=>99.14
);
for my $i( #a ) {
$b += $data{$i};
}
my $c = $b - (18 * ($x - 1));
return $c;
}

"Odd number of elements in hash assignment" - simple example code included, why does it happen?

Basically when I shift a hash to work with it in a subroutine I get the error: Odd number of elements in hash assignment. Am I supposed to use a hash reference instead if I wish to pass hashes to subroutines?
#!/usr/bin/perl -w
use strict;
my ($a, $b, $c, %hash) = &getVals() ;
&run($a,$b,$c,%hash) ;
sub getVals() {
$hash{"f"} = "abc" ;
$a = "A" ;
$b = "B" ;
$c = "C" ;
return ($a, $b, $c, %hash) ;
}
sub run() {
my $a = shift;
my $b = shift;
my $c = shift;
my %hash = shift; #error here
#do stuff here. . .
}
shift removes the first element from #_ and returns it. You can either use the reference, or just assign the whole list (after shifting the single elements) to the hash:
my %hash = #_;
It's impossible to pass hashes to subroutines. Subroutines can take a list of scalars as arguments. (It's also the only thing they can return.)
getVals returns 5 scalars:
A
B
C
f
abc
shift returns the first scalar in #_ after removing it. You want to assign all the remaining scalars in #_ (f and abc) to the hash, not just the first one.
sub run {
my $a = shift;
my $b = shift;
my $c = shift;
my %hash = #_;
...
}
or
sub run {
my ($a, $b, $c, %hash) = #_;
...
}
try this
#!/usr/bin/perl -w
use strict;
my ($a, $b, $c, %hash) = &getVals() ;
&run($a,$b,$c,%hash) ;
sub getVals() {
$hash{"f"} = "abc" ;
$a = "A" ;
$b = "B" ;
$c = "C" ;
return ($a, $b, $c, %hash) ;
}
sub run() {
my $a = shift;
my $b = shift;
my $c = shift;
my %hash = #_;
#.............
}

Uninitialized variable issue in Perl program

#!/usr/bin/perl
use warnings;
use Scalar::Util qw(looks_like_number);
sub term_value();
sub factor_value();
sub expression_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &term_value(#expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
print "$op\n";
if ($op eq "+" || $op eq "-")
{
$index++;
$value = &term_value(#expression, $index);
if ($op eq '+')
{
$result = $result + $value;
} else {
$result = $result - $value;
}
}
else
{
$more = 0;
}
}
return $result;
}
sub term_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &factor_value(#expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
if ($op eq "*" || $op eq "/")
{
$index++;
$value = &factor_value(#expression, $index);
if ($op eq '*')
{
$result = $result * $value;
} else {
$result = $result / $value;
}
} else {
$more = 0;
}
}
return $result;
}
sub factor_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = 0;
$c = $expression[$index];
if ($c eq '(')
{
$index++;
$result = &expression_value(#expression, $index);
$index++;
} else {
while (looks_like_number($c))
{
$result = 10 * $result + $c - '0';
$index++;
$c = $expression[$index];
}
}
return $result;
}
#Collect argument and separate by character
#one_char = split(//, $ARGV[0]);
$index = 0;
$result = &expression_value(#one_char, $index);
print $result . "\n";
My console returns these warnings:
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 25.
Use of uninitialized value $op in string eq at eval.pl line 25.
about the $op variable being uninitialized. I'm thinking this may be a scope problem...but I can't figure it out. I've tried everything I could think of (initializing the variable outside of the loop, etc.), but none of it seems to make a difference when running the program. Any suggestions would be greatly appreciated!
You're only using package (~global) variables, which is a huge problem given that you are using recursive functions! Start by adding
use strict;
Primarily, this will identify the variables you haven't declared. Use my to declare them in the appropriate scope.
You're trying to pass arrays to the subs, but you're failing. The only thing that can be passed to a sub is a list of scalars. If you want to pass an array to a sub, you'll need to pass a reference (~pointer) to the array.
sub foo {
my ($expressions, $index) = #_;
print($expressions->[$index], "\n");
}
foo(\#expressions, $index);
This is the reason you're getting the warnings. You are assigning one element to an array (#expression = $_[0]), then you try to index the second or later element.
By using prototype (), you're telling Perl the sub takes no arguments. Then you use & to tell Perl to ignore the prototype so you can pass arguments to your subs. Get rid of both the () after the sub names and & before sub calls.
my $more = 1;
while ($more) {
...
if (cond) {
...
} else {
$more = 0;
}
}
can be reduced to
while (1) {
...
last if !cond;
...
}
Higher Order Perl has a chapter on parsing. See section 8.1.2 for how you would build an expression parser and evaluator from scratch.
You can also take a look at the demo calculator script provided with Parse::RecDescent.
Just out of curiosity, I wanted to see what can be achieved without using parsers. The following script makes a lot of assumptions, but "works" for the simple cases.
#!/usr/bin/env perl
use strict;
use warnings;
use Regexp::Common qw(balanced number);
die "Need expression\n" unless #ARGV;
my ($expression) = #ARGV;
my $result = evaluate_expression($expression);
printf(
"'%s' evaluated to %g\n",
$expression, $result
);
my $expected = eval $expression;
unless ($result == $expected) {
die "Wrong result, should have been '$expected'\n";
}
sub evaluate_expression {
my ($expression) = #_;
my $n = qr!$RE{num}{real}!;
my $mul = qr![*/]!;
my $add = qr![+-]!;
my $subexpr = qr!$RE{balanced}{-parens=>'()'}{-keep}!;
1 while
$expression =~ s!
$subexpr
!
my $s = $1;
$s =~ s{(?:^\()|(?:\)\z)}{}g;
evaluate_expression($s)
!gex;
1 while
$expression =~ s!($n) \s* ($mul) \s* ($n)!"$1 $2 $3"!geex;
1 while
$expression =~ s!($n) \s* ($add) \s* ($n)!"$1 $2 $3"!geex;
return $expression;
}
Output:
C:\Temp> z "((1+1)*3 +2)*5"
'((1+1)*3 +2)*5' evaluated to 40
C:\Temp> z "(1+1)*3 + 2*5"
'(1+1)*3 + 2*5' evaluated to 16
But, of course, it's fragile:
C:\Temp> z "2*3+2*5"
'2*3+2*5' evaluated to 610
Wrong result, should have been '16'
As a bit of a corollary to Sinan's answer, here is a "parser" written from the other side of the camel.
use 5.010;
use strict;
use warnings;
my #ops;
use overload map {
my $op = $_;
$op => sub {
my ($x, $y) = #_[$_[2] ? (1, 0) : (0, 1)];
bless [$x, $op, $y]
}
} #ops = qw(+ - / *);
my %ops = map {$_ => eval "sub {\$_[0] $_ \$_[1]}"} #ops;
sub eval {
my $self = shift;
return $$self[0] if #$self == 1;
my ($x, $op, $y) = map {ref eq 'main' ? $_->eval : $_} #$self;
my $ret = $ops{$op}->($x, $y);
say "$ret = $x $op $y";
$ret;
}
BEGIN {overload::constant integer => sub {bless [$_[1]]}}
eval->eval for "#ARGV";
Which when run:
$ perl eval.pl 2*3+2*5
prints:
6 = 2 * 3
10 = 2 * 5
16 = 6 + 10