Perl How to use array store input strings - arrays

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.

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?

Perl Circle Array Function

I am searching for help on how to loop through an array. I have source code that takes a number for a radius of a circle and produces its circumference. This only takes one number, and I would like to get multiple circumferences.
#!/usr/bin/env perl
use warnings;
$pi = 3.141592654;
print "What's the radius? ";
chomp($radius = <STDIN>);
$circle = 2 * $pi * $radius;
if ($radius < 0 ) {
$circle = 0;
}
print "The circumference of a circle with the radius of $radius is $circle.\n";
Now I want to be able to input several numbers for multiple radii using a while loop to put them into an array. Once I enter 0 or a number less than 0 the while loop will exit and the program will continue by looping through the array of user entered numbers and calculate the circumference of each circle using those values as a radii.
I have some code that is trying to do this, but I cannot seem to get it to work.
#number = <STDIN>;
while(<>) {
print "whats the radius? ";
my($circle, $radius, $pi);
$radius = &rad(<STDIN>);
$pi = 3.141592654;
$circle = 2 * $pi * $radius;
if ($radius <= 0){
return $circle; }
}
Any direction of where to go, or a simple solution would be greatly appreciated.
To do that with an array, you need to first read until you get a zero and push. Then you can process the array using a for or foreach loop. Those two are the same, but the syntax commonly associated with foreach is more perlish.
use strict;
use warnings;
# turn on autoflush so stuff gets printed immediately
$|++;
print "Please enter a bunch of numbers, or 0 to stop. ";
my #numbers;
while ( my $n = <> ) {
chomp $n;
# stop at zero or less
last if $n <= 0;
# store the number in the array
push #numbers, $n;
}
my $pi = 3.141592654;
foreach my $radius (#numbers) {
my $circumference = 2 * $pi * $radius;
# print with format (%s gets replaced by params)
printf "The circumference of a circle with radius %s is %s\n", $radius, $circumference;
}
Here's the input/output.
Please enter a bunch of numbers, or 0 to stop. 1
2
3
0
The circumference of a circle with radius '1' is '6.283185308'
The circumference of a circle with radius '2' is '12.566370616'
The circumference of a circle with radius '3' is '18.849555924'
When running your first program, I noticed that the What's the radius? message showed up under the input prompt. That is because your output buffer only gets flushed when you have a newline \n in the output (or in some other cases, but let's ignore those). To change that behaviour, you can enable autoflush on the buffer, or simply set $| to a value larger than zero. The increment-idiom is common to do that.
You can also use the Math::Trig module to get π. It comes with Perl and has a constant that gives you pi. Since constants in Perl are just subs, you import it like any other function from a module.
use strict;
use warnings;
use Math::Trig 'pi'; # load module and import the 'pi' constant
# ...
foreach my $radius (#numbers) {
my $circumference = 2 * pi * $radius;
# print with format (%s gets replaced by params)
printf "The circumference of a circle with radius %s is %s\n", $radius, $circumference;
}
use strict;
use warnings;
use Data::Dumper;
This repeats your code in a infinite loop, exiting if the radius <= 0.
It stores all corresponding values for circle and radius in arrays:
my (#circle, #radius);
while(1){
print "What's the radius? ";
chomp($radius = <STDIN>);
$circle = 2 * $pi * $radius;
print "The circumference of a circle with the radius of $radius is $circle.\n";
last if $radius <= 0;
push #circle, $circle;
push #radius, $radius;
}
print Dumper \#circle, \#radius;
Easier than it might sound - tone of the key ways of interacting with an array is via a for (or foreach - they're the same) loop. Each iteration of the loop, a variable is set to the iterator - you can specify it (I have below) but if you don't it uses $_ by default.
#!/usr/bin/perl
use strict;
use warnings;
my $PI = 3.141592654;
print "Please enter radii, terminate with ^D\n";
my #list_of_values = <STDIN>;
chomp ( #list_of_values ); #remove linefeeds
foreach my $radius ( #list_of_values ) {
print "Circumference of radius $radius is: ", $PI * $radius * 2,"\n";
}
The other trick I use above - is reading STDIN. Perl knows the difference between reading a single value - into a scalar - or reading a sequence of values into an array.
So it'll behave differently if you:
my $value = <STDIN>; #reads one line
my #values = <STDIN>; #reads all the lines
You could also do this with:
#!/usr/bin/perl
use strict;
use warnings;
my $PI = 3.141592654;
my #list_of_values;
print "Enter radii, or \"done\" if complete\n";
while ( my $input = <STDIN> ) {
chomp($input);
last if $input eq "done";
if ( $input =~ m/^[\d\.]+$/ ) {
push( #list_of_values, $input );
}
else {
print "Input of \"$input\" is invalid, please try again\n";
}
}
foreach my $radius (#list_of_values) {
print "Circumference of radius $radius is: ", $PI * $radius * 2, "\n";
}
for and foreach do the same thing, it's a matter of style which you use.

Perl - can't use string (...) as an array ref

I'm practicing Perl with a challenge from codeeval.com, and I'm getting an unexpected error. The goal is to iterate through a file line-by-line, in which each line has a string and a character separated by a comma, and to find the right-most occurrence of that character in the string. I was getting wrong answers back, so I altered the code to print out just variable values, when I got the following error:
Can't use string ("Hello world") as an ARRAY ref while "strict refs" in use at char_pos.pl line 20, <FILE> line 1.
My code is below. You can see a sample from the file in the header. You can also see the original output code, which was incorrectly only displaying the right-most character in each string.
#CodeEval challenge: https://www.codeeval.com/open_challenges/31/
#Call with $> char_pos.pl numbers
##Hello world, d
##Hola mundo, H
##Keyboard, b
##Connecticut, n
#!/usr/bin/perl
use strict;
use warnings;
my $path = $ARGV[0];
open FILE, $path or die $!;
my $len;
while(<FILE>)
{
my #args = split(/,/,$_);
$len = length($args[0]) - 1;
print "$len\n";
for(;$len >= 0; $len--)
{
last if $args[0][$len] == $args[1];
}
#if($len > -1)
#{
# print $len, "\n";
#}else
#{
# print "not found\n";
#}
}
EDIT:
Based on the answers below, here's the code that I got to work:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
open my $fh,"<",shift;
while(my $line = <$fh>)
{
chomp $line;
my #args = split(/,/,$line);
my $index = rindex($args[0],$args[1]);
print $index>-1 ? "$index\n" : "Not found\n";
}
close $fh;
It looks like you need to know a bit about Perl functions. Perl has many functions for strings and scalars and it's not always possible to know them all right off the top of your head.
However, Perl has a great function called rindex that does exactly what you want. You give it a string, a substring (in this case, a single character), and it looks for the first position of that substring from the right side of the string (the index does the same thing from the left hand side.)
Since you're learning Perl, it may be a good idea to get a few books on Modern Perl and standard coding practices. This way, you know newer coding techniques and the standard coding practices.
Modern Perl - Gives you newer programming help.
Learning Perl - An old standard.
Perl Best Practices - The standard coding practices.
Here's a sample program:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
use feature qw(say);
open my $fh, "<", shift;
while ( my $line = <$fh> ) {
chomp $line;
my ($string, $char) = split /,/, $line, 2;
if ( length $char != 1 or not defined $string ) {
say qq(Invalid line "$line".);
next;
}
my $location = rindex $string, $char;
if ( $location != -1 ) {
say qq(The right most "$char" is at position $location in "$string".);
}
else {
say qq(The character "$char" wasn't found in line "$line".)";
}
close $fh;
A few suggestions:
use autodie allows your program to automatically die on bad open. No need to check.
Three parameter open statement is now considered de rigueur.
Use scalar variables for file handles. They're easier to pass into subroutines.
Use lexically scoped variables for loops. Try to avoid using $_.
Always do a chomp after a read.
And most importantly, error check! I check the format of the line to make sure that's there is only a single comma, and that the character I'm searching for is a character. I also check the exit value of rindex to make sure it found the character. If rindex doesn't find the character, it returns a -1.
Also know that the first character in a line is 0 and not 1. You may need to adjust for this depending what output you're expecting.
Strings in perl are a basic type, not subscriptable arrays. You would use the substr function to get individual characters (which are also just strings) or substrings from them.
Also note that string comparison is done with eq; == is numeric comparison.
while($i=<DATA>){
($string,$char)=split(",",$i);
push(#str,$string);}
#join=split("",$_), print "$join[-1]\n",foreach(#str);
__DATA__
Hello world, d
Hola mundo, H
Keyboard, b
Connecticut, n

Perl script problems

The purpose of the script is to process all words from a file and output ALL words that occur the most. So if there are 3 words that each occur 10 times, the program should output all the words.
The script now runs, thanks to some tips I have gotten here. However, it does not handle large text files (i.e. the New Testament). I'm not sure if that is a fault of mine or just a limitation of the code. I am sure there are several other problems with the program, so any help would be greatly appreciated.
#!/usr/bin/perl -w
require 5.10.0;
print "Your file: " . $ARGV[0] . "\n";
#Make sure there is only one argument
if ($#ARGV == 0){
#Make sure the argument is actually a file
if (-f $ARGV[0]){
%wordHash = (); #New hash to match words with word counts
$file=$ARGV[0]; #Stores value of argument
open(FILE, $file) or die "File not opened correctly.";
#Process through each line of the file
while (<FILE>){
chomp;
#Delimits on any non-alphanumeric
#words=split(/[^a-zA-Z0-9]/,$_);
$wordSize = #words;
#Put all words to lowercase, removes case sensitivty
for($x=0; $x<$wordSize; $x++){
$words[$x]=lc($words[$x]);
}
#Puts each occurence of word into hash
foreach $word(#words){
$wordHash{$word}++;
}
}
close FILE;
#$wordHash{$b} <=> $wordHash{$a};
$wordList="";
$max=0;
while (($key, $value) = each(%wordHash)){
if($value>$max){
$max=$value;
}
}
while (($key, $value) = each(%wordHash)){
if($value==$max && $key ne "s"){
$wordList.=" " . $key;
}
}
#Print solution
print "The following words occur the most (" . $max . " times): " . $wordList . "\n";
}
else {
print "Error. Your argument is not a file.\n";
}
}
else {
print "Error. Use exactly one argument.\n";
}
Your problem lies in the two missing lines at the top of your script:
use strict;
use warnings;
If they had been there, they would have reported lots of lines like this:
Argument "make" isn't numeric in array element at ...
Which comes from this line:
$list[$_] = $wordHash{$_} for keys %wordHash;
Array elements can only be numbers, and since your keys are words, that won't work. What happens here is that any random string is coerced into a number, and for any string that does not begin with a number, that will be 0.
Your code works fine reading the data in, although I would write it differently. It is only after that that your code becomes unwieldy.
As near as I can tell, you are trying to print out the most occurring words, in which case you should consider the following code:
use strict;
use warnings;
my %wordHash;
#Make sure there is only one argument
die "Only one argument allowed." unless #ARGV == 1;
while (<>) { # Use the diamond operator to implicitly open ARGV files
chomp;
my #words = grep $_, # disallow empty strings
map lc, # make everything lower case
split /[^a-zA-Z0-9]/; # your original split
foreach my $word (#words) {
$wordHash{$word}++;
}
}
for my $word (sort { $wordHash{$b} <=> $wordHash{$a} } keys %wordHash) {
printf "%-6s %s\n", $wordHash{$word}, $word;
}
As you'll note, you can sort based on hash values.
Here is an entirely different way of writing it (I could have also said "Perl is not C"):
#!/usr/bin/env perl
use 5.010;
use strict; use warnings;
use autodie;
use List::Util qw(max);
my ($input_file) = #ARGV;
die "Need an input file\n" unless defined $input_file;
say "Input file = '$input_file'";
open my $input, '<', $input_file;
my %words;
while (my $line = <$input>) {
chomp $line;
my #tokens = map lc, grep length, split /[^A-Za-z0-9]+/, $line;
$words{ $_ } += 1 for #tokens;
}
close $input;
my $max = max values %words;
my #argmax = sort grep { $words{$_} == $max } keys %words;
for my $word (#argmax) {
printf "%s: %d\n", $word, $max;
}
why not just get the keys from the hash sorted by their value and extract the first X?
this should provide an example: http://www.devdaily.com/perl/edu/qanda/plqa00016

Perl - how to avoid warnings about uninitialized elements of an array?

#!/usr/bin/perl
use strict;
use warnings;
sub paragraph
{
open my $file, "<", "dict.txt" or die "$!";
my #words = <$file>;
close $file;
print "Number of lines:";
my $lines = <>;
print "Max words per line:";
my $range = <>;
for(my $i = 0; $i<$lines; $i++){
my $wordcount = int(rand($range));
for(my $s = 0; $s<=$wordcount; $s++){
my $range2 = scalar(#words);
my $word = int(rand($range2));
print $words[$word]." ";
if($s==$wordcount){
print "\n";}
}
}
}
paragraph;
I'm trying to learn programming, so I just wrote this simple script.
When running this code, I am getting use of uninitialized value errors... I can't figure out why, but I sure I am just overlooking something.
These two lines open the dict.txt file for writing and then try to read from it.
open FILE, ">dict.txt" or die $!;
my #words = <FILE>;
Since you can't read from a write-only file, it fails. If the file was writable, then it is empty now - sorry about your nice word list. Suggestion:
open my $file, "<", "dict.txt" or die "$!";
my #words = <$file>;
close $file;
Also, please learn to indent your braces in an orthodox fashion, such as:
sub go
{
print "Number of lines:";
my $lines = <>;
print "Max words per line:";
my $range = <>;
for (my $i = 0; $i<$lines; $i++){
my $wordcount = int(rand($range));
for (my $s = 0; $s<$wordcount; $s++){
my $range2 = 23496;
my $word = int(rand($range2));
my $chosen = #words[$word];
print "$chosen ";
if ($s=$wordcount){
print "\n";
}
}
}
}
Also leave a space between 'if' or 'for' and the open parenthesis.
Your assignment if ($s = $wordcount) probably isn't what you intended; however, the condition if ($s == $wordcount) will always be false since it is in the scope of a loop with the condition $s < $wordcount. You need to rethink that part of your logic.
On average, you should choose a better name for your function than go. Also, it is probably better to invoke it as go();.
When I test compile your script, Perl warns about:
Scalar value #words[$word] better written as $words[$word] at xx.pl line 19.
You should fix such errors before posting.
You have:
my $range2 = 23496;
my $word = int(rand($range2));
Unless you have more than 23,496 words in your dictionary, you will likely be accessing an uninitialized word. You should probably use:
my $range2 = scalar(#words);
That then just leaves you with some logic problems to resolve.
Given 'dict.txt' containing:
word1
word2
word3
word4
nibelung
abyssinia
tirade
pearl
And 'xx.pl' containing:
#!/usr/bin/env perl
use strict;
use warnings;
open my $file, "<", "dict.txt" or die $!;
my #words = <$file>;
close $file;
sub go
{
print "Number of lines: ";
my $lines = <>;
print "Max words per line: ";
my $range = <>;
my $range2 = scalar(#words);
for (1..$lines)
{
for (1..$range)
{
my $index = int(rand($range2));
my $chosen = $words[$index];
chomp $chosen;
print "$chosen ";
}
print "\n";
}
}
go();
When I run it, I get:
$ perl xx.pl
Number of lines: 3
Max words per line: 4
word4 word3 word4 nibelung
abyssinia pearl word1 tirade
word3 word1 word3 word2
$
Some more bugs:
if($s=$wordcount){
You need == here.

Resources