Perl: Why can I call subroutine A recursively, but I can't call A from B, which is called from an earlier A? - perl

Try to "Play again" by answering Y when asked. Or am I just missing something completely obvious?
#!/usr/bin/perl
sub calc() {
$circ = 2 * $radius * 3.141592654;
print "The circumference is $circ\nPlay again (Y/n)? ";
$again = <STDIN>;
if ($again eq "Y") {
dialog();
} else {print "Bye!\n";
}
}
sub dialog {
print "What's the radius of the circle you'd like the circumference of?\n> ";
$radius = <STDIN>;
if ($radius eq "\n") {
print "That was just a blank line!\n";
dialog();
} elsif ($radius < 0) {
print "That was negative! Let's assume radius is 0.\n";
$radius = 0;
calc();
} else {calc();
}
}
dialog();

You don't chomp your input from STDIN
chomp($again = <STDIN>);
Also, always include use strict; and use warnings; at the top of each and EVERY perl script.
Finally, instead of doing recursion, a simple loop is sufficient. The following is a cleaned up version of your code:
#!/usr/bin/perl
use strict;
use warnings;
while (1) {
print "What's the radius of the circle you'd like the circumference of?\n> ";
chomp(my $radius = <STDIN>);
if ($radius eq "") {
print "That was just a blank line!\n";
next;
}
if ($radius < 0) {
print "That was negative! Let's assume radius is 0.\n";
$radius = 0;
}
my $circ = 2 * $radius * 3.141592654;
print "The circumference is $circ\nPlay again (Y/n)? ";
chomp(my $again = <STDIN>);
if ($again ne "Y") {
print "Bye!\n";
last;
}
}

Related

How can I calculate the geometric center of a protein in Perl?

I have a PDB file which contains information about a specific protein. One of the information it holds is the positions of the different atoms (xyz coordinates).
The file is the following https://files.rcsb.org/view/6U9D.pdb . With this file I want to calculate the geometric center of the atoms. In theory I know what I need to do, but the script I wrote does not seem to work.
The first part of the program, before the for loop, is another part of the assignment which requires me to read the sequence and convert it from the 3 letter nomenclature to the 1 letter one. The part that interests me is the for loop until the end. I tried to pattern match in order to isolate the XYZ coordinates. Then I used a counter that I had set up in the beginning which is the $k variable. When I check the output on cygwin the only output I get is the sequence 0 0 0 instead of the sum of each dimension divided by $k. Any clue what has gone wrong?
$k=0;
open (IN, '6U9D.pdb.txt');
%amino_acid_conversion = (
ALA=>'A',TYR=>'Y',MET=>'M',LEU=>'L',CYS=>'C',
GLY=>'G',ARG=>'R',ASN=>'N',ASP=>'D',GLN=>'Q',
GLU=>'E',HIS=>'H',TRP=>'W',LYS=>'K',PHE=>'F',
PRO=>'P',SER=>'S',THR=>'T',ILE=>'I',VAL=>'V'
);
while (<IN>) {
if ($_=~m/HEADER\s+(.*)/){
print ">$1\n"; }
if ($_=~m/^SEQRES\s+\d+\s+\w+\s+\d+\s+(.*)/){
$seq.=$1;
$seq=~s/ //g;
}
}
for ($i=0;$i<=length $seq; $i+=3) {
print "$amino_acid_conversion{substr($seq,$i,3)}";
if ($_=~m/^ATOM\s+\d+\s+\w+\s+\w+\s+\w+\s+\d+\s+(\S+)\s+(\S+)\s+(\S+)/) {
$x+=$1; $y+=$2; $z+=$3; $k++;
}
}
print "\n";
#print $k;
$xgk=($x/$k); $ygk=($y/$k); $zgk=($z/$k);
print "$xgk $ygk $zgk \n";
I do not know bioinformatics but it seems like you should do something like this:
use feature qw(say);
use strict;
use warnings;
my $fn = '6U9D.pdb';
open ( my $IN, '<', $fn ) or die "Could not open file '$fn': $!";
my $seq = '';
my $x = 0;
my $y = 0;
my $z = 0;
my $k = 0;
while (<$IN>) {
if ($_ =~ m/HEADER\s+(.*)/) {
say ">$1";
}
if ($_=~m/^SEQRES\s+\d+\s+\w+\s+\d+\s+(.*)/){
$seq .= $1;
}
if ($_ =~ m/^ATOM\s+\d+\s+\w+\s+\w+\s+\w+\s+\d+\s+(\S+)\s+(\S+)\s+(\S+)/) {
$x+=$1; $y+=$2; $z+=$3; $k++;
}
}
close $IN;
$seq =~ s/ //g;
my %amino_acid_conversion = (
ALA=>'A',TYR=>'Y',MET=>'M',LEU=>'L',CYS=>'C',
GLY=>'G',ARG=>'R',ASN=>'N',ASP=>'D',GLN=>'Q',
GLU=>'E',HIS=>'H',TRP=>'W',LYS=>'K',PHE=>'F',
PRO=>'P',SER=>'S',THR=>'T',ILE=>'I',VAL=>'V'
);
my %unknown_keys;
my $conversion = '';
say "Sequence length: ", length $seq;
for (my $i=0; $i < length $seq; $i += 3) {
my $key = substr $seq, $i, 3;
if (exists $amino_acid_conversion{$key}) {
$conversion.= $amino_acid_conversion{$key};
}
else {
$unknown_keys{$key}++;
}
}
say "Conversion: $conversion";
say "Unknown keys: ", join ",", keys %unknown_keys;
say "Number of atoms: ", $k;
my $xgk=($x/$k);
my $ygk=($y/$k);
my $zgk=($z/$k);
say "Geometric center: $xgk $ygk $zgk";
This gives me the following output:
[...]
Number of atoms: 76015
Geometric center: 290.744642162734 69.196842162731 136.395842938893

Perl transfer scalar to subroutine by parameter passing

How do I transfer a scalar to a subroutine by parameter passing? I have written the following code and want to pass the $radius from sub get_radius to sub area_circle.
#!/usr/bin/env perl
use warnings;
use strict;
use Math::Trig ':pi';
sub get_radius {
print "Enter the radius of the circle: \n";
my $radius = <STDIN>;
}
sub area_circle {
my $radius = get_radius();
my $area = 0;
$area = pi * ($radius **2);
return $area;
}
my $area = area_circle;
print "The area is: $area \n";
subs take their parameters from the #_ array, like this:
sub whatever {
my ($param1, $param2) = #_;
}
or
sub whatever {
my $param1 = $_[0];
my $param2 = $_[1];
}
In the context of your code:
#!/usr/bin/env perl
use warnings;
use strict;
use Math::Trig ':pi';
sub get_radius {
print "Enter the radius of the circle: \n";
my $radius = <STDIN>;
return $radius;
}
sub area_circle {
my ($radius) = #_;
my $area = 0;
$area = pi * ($radius **2);
return $area;
}
my $radius = get_radius;
my $area = area_circle( $radius );
print "The area is: $area \n";
Note how the radius is now being passed in to area_circle, so that area_circle is now no longer tied to the get_radius sub and can now calculate the area of a circle no matter where the radius is fetched from.
You can use any of the following methods...
#!/usr/bin/env perl
use warnings;
use strict;
use Math::Trig ':pi';
my $radius = 0; #####
sub get_radius {
print "Enter the radius of the circle: \n";
$radius = <STDIN>;
}
sub area_circle {
get_radius(); #####
my $area = 0;
$area = pi * ($radius **2);
return $area;
}
my $area = area_circle;
print "The area is: $area \n";
OR
#!/usr/bin/env perl
use warnings;
use strict;
use Math::Trig ':pi';
sub get_radius {
print "Enter the radius of the circle: \n";
my $radius = <STDIN>;
return $radius
}
sub area_circle {
my $radius = get_radius();
my $area = 0;
$area = pi * ($radius **2);
return $area;
}
my $area = area_circle;
print "The area is: $area \n";

Perl. How can I make output disappear after several seconds?

I'm prompting a user for a correct answer for example:
/> 13 + 7 ?
is it any way of making this output disappear after 2 seconds for example ?
..thanks' for any suggestions
You're asking for a few things combined, I think:
1) how do you erase a line
2) how do you wait for input for a while and then give up on waiting (ie, a timer)
The following code will do what you want (there are other ways of accomplishing both of the above, but the below shows one way for each of the above tasks):
use strict; use warnings;
use IO::Select;
my $stdin = IO::Select->new();
$stdin->add(\*STDIN);
# always flush
$| = 1;
my $question = "/> 7 + 3 ? ";
print $question;
if ($stdin->can_read(2)) {
print "you entered: " . <STDIN>;
} else {
print "\010" x length($question);
print " " x length($question);
print "too late\n";
}
Use select on STDIN to see whether there is any input within 2 seconds. If not, overwrite the output with a carriage return (\r) or multiple backspaces (\b).
Proof of concept:
$| = 1; # needed because print calls don't always use a newline
$i = int(rand() * 10);
$j = int(rand() * 10);
$k = $i + $j;
print "What is $i + $j ? ";
$rin = '';
vec($rin, fileno(STDIN), 1) = 1;
$n = select $rout=$rin, undef, undef, 2.0;
if ($n) {
$answer = <STDIN>;
if ($answer == $k) {
print "You are right.\n";
} else {
print "You are wrong. $i + $j is $k\n";
}
} else {
print "\b \b" x 15;
print "\n\n";
print "Time's up!\n";
sleep 1;
}
When you are ready for a more advanced solution, you could probably check out Term::ReadKey (so you don't have to hit Enter after you type in your answer) or something like Curses to exercise more control over writing to arbitrary spots on your terminal.

String matching in perl (if statement in for loop) [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have following code
#!C:\Perl64\bin -w
#use strict; use warnings;
init_words();
print "What is your name Mr. \n";
$name = <STDIN>;
chomp ($name);
if ($name =~ /^randal\b/i){
print "Hello, Randal, How are you doing \n";
} else {
print "Hello, $name!\n";
print "Tell the secret word\n";
$guess = <STDIN>;
chomp ($guess);
while (!good_word ($name,$guess)) {
print "Wrong, please try again\n";
$guess = <STDIN>;
chomp ($guess);
}
}
sub init_words {
open (WORDSLIST, "wordslist.txt") || die "can't open wordslist: $!";
$k = 1;
$a = 0;
$b = 0;
while (defined ($name = <WORDSLIST>)) {
if ($k % 2 == 0) {
chomp ($name);
$words1[$a] = $name;
++$k;
++$a;
} else {
chomp ($name);
$words2[$b] = $name;
++$k;
++$b;
}
}
close (WORDSLIST) || die "couldn't close wordlist: $!";
}
sub good_word {
my ($somename, $someguess) = #_;
$somename =~ s/\W.*//;
$somename =~ tr/A-Z/a-z/;
if ($somename eq "randal") {
return 1;
} else {
#$n = 0;
#words1 has secret words.
#words2 has names.
$t = scalar #words1;
$u = scalar #words2;
print "the words1 array is #words1 \n";
print "the words2 array is #words2 \n";
for ($d = 0; $d < $u; $d++) {
#print "currently name in array is #words2[$d]\n";
print "The value of somename is $somename \n";
$delta = $words2[$d];
print "The value of delta is $delta";
#use strict; use warnings;
if ($delta eq '$somename') {
print "test";
return 1;
}
}
#print "The final value of d is $d";
#print " The final value of array is #words1[$d]";
#if ("groucho" eq $someguess) {
#return 1;}
#else{
#while ($n < $t){
#if (#words1[$n] eq $someguess) {
#return 1;}
#else { ++$n};
}
The main goal of the code is to have wordslist defined. The code should split the wordslist into two sublists i.e. #words1 and #words2. User is asked for a name and then secret guess. The code should check for the name in the #words2 and if match is found program exit (with printing test).
For some reason, it is not working as expected. I tried doing some basic debugging and everything looks ok but in the function good_word, the if statement under for loop is never returned true although i can see in my debugging that both $somename and $delta are same.
Any suggestions??
Change
if ($delta eq '$somename'){
to
if ($delta eq $somename){
Perl strings with double quotes (") will interpolate variables like $somename but strings with single quotes (') will not do that.
Reference to documentation about that: http://perldoc.perl.org/perlop.html#Quote-and-Quote-like-Operators

Simple compiler Errors: Too new to Perl to know how to fix them

Hello I just began learning Perl. I have this simple temperature application but it is not compiling, I dont understand what are the errors & how I can fix them?
Can you point out what I am doing wrong & how I fix them:
#!/usr/local/bin/perl
use strict;
use warnings;
use constant false >= 0;
use constant true >= 1;
# Experimentation: Array & Hashes:
my #ar = ("a", "b", "c"); # error here
print $ar, "\n";
print $ar[0], "\n";
print $#ar, "\n";
print "The size of array = $#ar \n";
print "Trying to print $ar[1] within a string: ", $ar[1], " \n";
my %ha = ( "a" => 1, "b", "c", "d" ); # error here
print $ha, "\n";
print $ha{"a"}, "\n";
print $#ha, "\n";
print "The size of hash = $#ha \n";
print "Trying to print $ha{'b'} within string: ", $ha{'b'}, " \n";
# Functions:
sub isfloat
{
my $val = shift;
return $val =~ m/^\d+.\d+$/;
}
sub toFahrenheit
{
my $val = #_[0];
# if param var is a float
if ( isFloat($val) == 0 ) # Error here: Scalar value #_[0] better written as $_[0]
{
return -1;
}
return ( ($val * (9/5)) + 32 );
}
sub toCelsius
{
if ( isFloat(#_[0]) == 0 )
{
return -1;
}
return ( (#_[0] - 32) * (5/9) );
}
# Main Program:
my $programEnd = 0;
while ( $programEnd == 0 )
{
my $menu = "*** Welcome to Temperature Converter *** \n\n1. Convert from Celsius to Fahrenheit \n2. Convert from Fahrenheit to Celsius \n3. Exit \n Enter decision (1,2 or 3): ";
print $menu;
my $decision = <>;
if ( $decision == 3 )
{
$programEnd = 1;
# Could also just do this
# break;
}
print "Please enter a number: ";
my $val = <>;
if ( isfloat($val) )
{
$conVal = -1;
if ( decision == 1 )
{
$conVal = toFahrenheit( $val );
print $val, " C in Fahrenheit is: ", $conVal, " F \n";
}
else
{
$conVal = toCelsius( $val );
print $val, " F in Celsius is: ", $conVal, " C \n";
}
}
else { print $val, " is not a number \n"; }
}
The unhelpful messages about "Subroutine BEGIN redefined" actually is caused by these two lines:
use constant false >= 0;
use constant true >= 1;
You mean them to be:
use constant false => 0;
use constant true => 1;
The error "Scalar value #_[0] better written as $_[0]" is because in perl when you refer to an element of an array #arr, you say $arr[0], not #arr[0], because the element itself is a scalar.
>= ≠ =>