Testing numeric user input against array elements - arrays

I'm making a simple fortune teller program for a class. I wanted to start by having the user choose between four numbers, like on a paper fortune teller (cootie catcher, as it were).
I created an array, #number_choices = ( 1, 2, 3, 4 ); and I wanted to make sure the user input was equal to one of the numbers in the array. Here is what I have so far, which is not working at all (when I run the program, it prints the error message no matter what number I enter, except for sometimes 2 or 1):
my $number_chosen = <STDIN>;
chomp ($number_chosen);
my $num;
my $found = 0;
while ( $found == 0 )
{
foreach $num (#number_choices)
{
if ($number_chosen == $num)
{
$found = 1;
last;
}
else
{
print "I'm sorry, that number is not valid. Please pick a number: " . join(', ', #number_choices) . "\n";
$number_chosen = <STDIN>;
chomp ($number_chosen);
}
}
}

You have a logic error. You choose a number but for every loop through the array it makes you choose another one, so if in the second loop you enter number 2 it will be right regardless of your first input, and same number 3 for third loop.
You should put the error message out of the foreach loop and ask for a number inside the while. Besides that, the last only goes out of the nearest loop, so you need to use a label to go out of the while. Something similar to following dirty solution:
my #number_choices = (1, 2, 3, 4);
my $num;
my $found = 0;
LOOP:
while ( $found == 0 )
{
$number_chosen = <STDIN>;
chomp ($number_chosen);
foreach $num (#number_choices)
{
if ($number_chosen == $num)
{
$found = 1;
last LOOP;
}
}
print "I'm sorry, that number is not valid. Please pick a number: " . join(', ', #number_choices) . "\n";
}

Related

Perl Program to Count Two Character Frequencies

I am trying to find two character strings in a text file and print them and their frequencies out.
#!/usr/bin/perl
#digram finder
use strict; use warnings;
#finds digrams in a file and prints them and their frequencies out
die "Must input file\n" if (#ARGV != 1);
my ($file) = #ARGV;
my %wordcount;
open (my $in, "<$file") or die "Can't open $file\n";
while (my $words = <$in>){
chomp $words;
my $length = length($words);
for (my $i = 0; $i<$length; $i++){
my $duo = substr($words, $i; 2);
if (not exists $wordcount{$duo}){
$wordcount{$duo} = 1;
}
else {
$wordcount{$duo}++;
}
}
}
foreach my $word (sort {$wordcount{$b} cmp $wordcount{$a}} keys %wordcount){
print "$word\t$wordcount{$duo}\n";
}
close($in);
First I set the text file to a string $words.
Then, I run a for loop and create a substring $duo at each position along $words
If $duo doesn't exist within the hash %wordcount, then the program creates the key $duo
If $duo does exist, then the count for that key goes up by 1
Then the program prints out the digrams and their frequencies, in order of decreasing frequency
When I try to run the code, I get the error message that I forgot to declare $word on line 17 but I do not even have the string $word. I am not sure where this error message is coming from. Can someone help me find where the error is coming from?
Thank you
My best guess is that you actually have $word instead of $words; a typo. If the compilation found the symbol $word in the text then it's probably there.
However, I'd also like to comment on the code. A cleaned up version
while (my $words = <$in>) {
chomp $words;
my $last_duo_idx = length($words) - 2;
for my $i (0 .. $last_duo_idx) {
my $duo = substr($words, $i, 2);
++$wordcount{$duo};
}
}
my #skeys = sort { $wordcount{$b} <=> $wordcount{$a} } keys %wordcount;
foreach my $word (#skeys) {
print "$word\t$wordcount{$word}\n";
}
This runs correctly on a made-up file. (I sort separately only so to not run off of the page.)
Comments
Need to stop one before last in the line, and substr starts from 0; thus -2
One almost never needs a C-style loop
There is no need here to test for existence of a key. If it doesn't exist it is autovivified (created), then incremented to 1 with ++; otherwise the count is incremented.
To sort numerically use <=>, not cmp
Typos:
substr($words, $i; 2) needs a , not ;, so substr($words, $i, 2)
$wordcount{$duo} in print should be $wordcount{$word}.
I am not sure about naming: why is a line of text called $words?

Counting how many numbers greater than 5 in a given array

I am having an error saying that prototype not terminated at filename.txt line number 113 where as line number 113 belongs to a different program which is running successfully.
sub howmany(
my #H = #_;
my $m = 0;
foreach $x (#H) {
if ( $x > 5 ) {
$m +=1;
}
else {
$m +=0;
}
}
print "Number of elements greater than 5 is equal to: $m \n";
}
howmany(1,6,9);
The sub keyword should be followed by { } not ( ) (if you define a simple function), that's why the error
prototype not terminated
After this, always start with : use strict; use warnings;
Put this and debug your script, there's more errors.
Last but not least, indent your code properly, using an editor with syntax highlighting, you will save many time debugging
The error is due to parenthesis.
Never do $m += 0; As you actually load processor for nothing. Of course it's not gonna be visible on such a small function, but...
sub howmany {
my $m = 0;
foreach (#_) {
$m++ if ($_ > 5);
}
print "Number of elements greater than 5 is equal to: $m \n";
}
howmany(1,6,9);

Perl How to use array store input strings

User first enter a number of lines. It then reads n lines of text from user input, and prints these lines backwards, i.e., if n=5, it prints the 5th
line first, the 4th, …, and the 1st,line last. I don't think wrong in my code, but when i start enter lines(line end with a newline), it won't stop. my array can store infinite number of lines which cause the problem stop them
use strict;
use warnings;
print "Enter number of n \n";
my $n = <STDIN>;
print "Enter couple lines of text \n";
my #array = (1..$n);
#array=<STDIN>;
do{
print "$array[$n]";
$n--;
}until($n=0);
Below are some tips that might help you:
Always include use strict; and use warnings at the top of your scripts to enforce good coding practices and help you find syntax errors sooner.
You must chomp your input from <STDIN> to remove return characters.
Use the array function push to add elements to the end of an array. Other relevant array functions include pop, shift, unshift, and potentially splice.
Use reverse to return an array of reversed order.
Finally, the for my $element (#array) { can be a helpful construct for iterating over the elements of an array.
These tips lead to the following code:
use strict;
use warnings;
print "Enter number of n \n";
chomp(my $n = <STDIN>);
die "n must be an integer" if $n =~ /^\d+$/;
my #array;
print "Enter $n lines of text\n";
for (1..$n) {
my $input = <STDIN>;
# chomp $input; # Do you want line endings, or not?
push #array, $input;
}
print "Here is your array: #array";
The end loop condition is do { … } until ($n = 0);, which is equivalent to do { … } while (!($n = 0));
The trouble is that the condition is an assignment, and zero is always false, so the loop is infinite.
Use do { … } until ($n == 0);.
Note that the input is in the line #array = <STDIN>; — it reads all the input provided until EOF. It is not constrained by the value of $n in any way.
#!/usr/bin/env perl
use strict;
use warnings;
print "Enter number of n \n";
my $n = <STDIN>;
printf "Got %d\n", $n;
print "Enter couple lines of text \n";
my #array = (1..$n);
#array = <STDIN>;
do {
print "$n = $array[$n]";
$n--;
} until ($n == 0);
When run, that gives:
$ perl inf.pl
Enter number of n
3
Got 3
Enter couple lines of text
abc
def
ghi
jkl
^D
3 = jkl
2 = ghi
1 = def
$
I think your expectations are skewiff.

Perl: how to test if every element in an array is greater than a value?

I need to test a file with each line having the same number of columns and each entry is some value and I want to only select those lines with every values greater than, say 0.5.
I know I can loop through the array in each line by doing something like this:
open (IN, shift #ARGV);
while (<IN>){
chomp;
my $count = 0;
my #array = split/\t/;
foreach (#array){
if ($_ > 0.5) {
$count ++;
}
}
if ($count == scalar #array){
print $_,"\n";
}
}
close IN;
This is kinda long and I'm wondering if there is a better way to do it?
Thanks.
Use all from List::Util - it checks if passed code block (often with condition) returns true for all elements of list.
use List::Util qw(all);
if (all { $_ > 0.5 } #array) {
print "Pass!"
}
It will even short-circuit for you, terminating as soon as it finds first false value, producing most speed-effective result.
my #array = 10 .. 20;
# compare size of array with list size of grep
if (#array == grep { $_ > 0.5 } #array) {
print "All are greater than 0.5\n";
}

I can't access hash values

I have a program that creates an array of hashes while parsing a FASTA file. Here is my code
use strict;
use warnings;
my $docName = "A_gen.txt";
my $alleleCount = 0;
my $flag = 1;
my $tempSequence;
my #tempHeader;
my #arrayOfHashes = ();
my $fastaDoc = open(my $FH, '<', $docName);
my #fileArray = <$FH>;
for (my $i = 0; $i <= $#fileArray; $i++) {
if ($fileArray[$i] =~ m/>/) { # creates a header for the hashes
$flag = 0;
$fileArray[$i] =~ s/>//;
$alleleCount++;
#tempHeader = split / /, $fileArray[$i];
pop(#tempHeader); # removes the pointless bp
for (my $j = 0; $j <= scalar(#tempHeader)-1; $j++) {
print $tempHeader[$j];
if ($j < scalar(#tempHeader)-1) {
print " : "};
if ($j == scalar(#tempHeader) - 1) {
print "\n";
};
}
}
# push(#arrayOfHashes, "$i");
if ($fileArray[$i++] =~ m/>/) { # goes to next line
push(#arrayOfHashes, {
id => $tempHeader[0],
hla => $tempHeader[1],
bpCount => $tempHeader[2],
sequence => $tempSequence
});
print $arrayOfHashes[0]{id};
#tempHeader = ();
$tempSequence = "";
}
$i--; # puts i back to the current line
if ($flag == 1) {
$tempSequence = $tempSequence.$fileArray[$i];
}
}
print $arrayOfHashes[0]{id};
print "\n";
print $alleleCount."\n";
print $#fileArray +1;
My problem is when the line
print $arrayOfHashes[0]{id};
is called, I get an error that says
Use of uninitialized value in print at fasta_tie.pl line 47, line 6670.
You will see in the above code I commented out a line that says
push(#arrayOfHashes, "$i");
because I wanted to make sure that the hash works. Also the data prints correctly in the
desired formatting. Which looks like this
HLA:HLA00127 : A*74:01 : 2918
try to add
print "Array length:" . scalar(#arrayOfHashes) . "\n";
before
print $arrayOfHashes[0]{id};
So you can see, if you got some content in your variable. You can also use the module Data::Dumper to see the content.
use Data::Dumper;
print Dumper(\#arrayOfHashes);
Note the '\' before the array!
Output would be something like:
$VAR1 = [
{
'sequence' => 'tempSequence',
'hla' => 'hla',
'bpCount' => 'bpCount',
'id' => 'id'
}
];
But if there's a Module for Fasta, try to use this. You don't have to reinvent the wheel each time ;)
First you do this:
$fileArray[$i] =~ s/>//;
Then later you try to match like this:
$fileArray[$i++] =~ m/>/
You step through the file array, removing the first "greater than" sign in the line on each line. And then you want to match the current line by that same character. That would be okay if you only want to push the line if it has a second "greater than", but you will never push anything into the array if you only expect 1, or there turns out to be only one.
Your comment "puts i back to the current line" shows what you were trying to do, but if you only use it once, why not use the expression $i + 1?
Also, because you're incrementing it post-fix and not using it for anything, your increment has no effect. If $i==0 before, then $fileArray[$i++] still accesses $fileArray[0], only $i==1 after the expression has been evaluated--and to no effect--until being later decremented.
If you want to peek ahead, then it is better to use the pre-fix increment:
if ($fileArray[++$i] =~ m/>/) ...

Resources