Issue with goto statement along with if else condition - perl

I am trying to create a simple perl assignment for equality check and goto label concept.
User enters numbers , equality check happens, user is asked if they want to check more , if yes then it repeats, else it exits. Using "goto " for this
Issue- y/n checks for repeating,y is for repeating Label. even if I enter "n" it keeps going to the label Loop .
Why is the "if" condition containing "goto" not getting honored?
Code below
#Checking Equality
Loop: print "\Checking Equality\n";
print "Enter number for variable a\n";
$a = <stdin>;
print "Enter number for variable b\n";
$b = <stdin>;
if ( $a == $b ) {
print 'a and b are equal';
print "\n\n";
}
else {
print 'a and b are not equal';
}
print "\n\n";
print "do you want to check more? Enter y/n\n";
$c = <stdin>;
if ( $c == "y" ) {
goto Loop;
}
elsif ( $c == "n" ) {
print "Exiting\n";
}
Output-
Checking Equality
Enter number for variable a
3
Enter number for variable b
4
a and b are not equal
do you want to check more? Enter y/n
n
Checking Equality #### despite of entering n goto Loop is getting executed
Enter number for variable a

Just use chomp function to remove newline(s), and use eq comparison for string.
use strict;
use warnings;
Loop: print "\nChecking Equality\n";
print "Enter number for variable a\n";
my $a = <stdin>;
print "Enter number for variable b\n";
my $b = <stdin>;
if ( $a == $b ) {
print 'a and b are equal';
print "\n\n";
}
else {
print 'a and b are not equal';
}
print "\n\n";
print "do you want to check more? Enter y/n\n";
chomp(my $c = <stdin>);
if ( $c eq "y" ) {
goto Loop;
}
else {
print "Exiting\n";
}

You have chosen not best approach to use a label for goto.
Instead loop would be more appropriate to perform user input. User stays in the loop until he/she will not specify that he/she ready to leave.
User's input also should be stripped off newline symbol \n before it can be used for comparison.
As user suppose to provide input several time then it would be beneficial to use small subroutine to print 'input prompt', collect input, strip newline symbol and return input value. By doing so the program becomes shorter and easier to read.
String comparison operation performed with eq, number comparison performed with ==.
use strict;
use warnings;
use feature 'say';
my $answer = 'y';
while( $answer eq 'y' ) {
my $num_1 = get_input('Enter variable num_1: ');
my $num_2 = get_input('Enter variable num_2: ');
my $compare = $num_1 == $num_2 ? 'EQUIAL' : 'NOT EQUIAL';
say "\nNumbers are $compare\n";
$answer = get_input('Would you like to continue? (y/n) ');
$answer = lc $answer;
say '-' x 35;
}
sub get_input {
my $msg = shift;
my $input;
print $msg;
$input = <>;
chomp $input;
return $input;
}

Related

Wrong check with Strings

I'm new to Perl and can't understand, why this check has the output false:
// $answer would be a Global Variable
my $answer;
// the rest is in a sub scope
print "Do you want to proceed? (Y/N)";
$answer = uc <STDIN>;
if($answer eq "Y"){
print "success";
} else {
print "why";
}
Look at what the value of $answer actually is:
print "---$answer---";
It outputs
---Y
---
So it isn't equal to "Y", it is equal to "Y\n".
Use chomp on the string.

perl - Trying to use a while loop to ask the user if they want to do that again

(New to perl)
I have a small perl program that calculates factorials. I'd like to use a while loop so that after the user gets a result, they will be asked "Calculate another factorial? Y/N" and have Y run the code again & have N end the program.
Here's my code:
print"Welcome! Would you like to calculate a factorial? Y/N\n";
$decision = <STDIN>;
while $decision == "Y";
{
print"Enter a positive # more than 0: \n";
$num = <STDIN>;
$fact = 1;
while($num>1)
{
$fact = $fact * $num;
$num $num - 1;
}
print $fact\n;
print"Calculate another factorial? Y/N\n";
$decision = <STDIN>;
}
system("pause");
What's giving me trouble is where to put the while loop and how to make the Y/N option work. I'm also unclear about system("pause") and sleep functions. I do know that system("pause") makes my programs work though.
Your program is almost right, just a few issues:
Please get used to always add use strict; and use warnings; to your scripts. They will
(beyond other things) force you to declare all the variables you use (with my $num=…;)
and warn you about common errors (like typos). Some people consider it a bug that
use strict; and use warnings; aren't turned on by default.
When reading a line from STDIN (or some other filehandle) the read line will contain the
trailing newline character "\n". For your comparison to work you must get rid of that using
the chomp function.
There are two different sets of comparison operators in Perl: one for strings and one for numbers.
Numbers are compared with <, >, <=, >=, ==, and !=. For strings you must use
lt (less-than), gt, le (less-or-equal), ge, eq, and ne. If you use one of the number
operators on strings Perl will try to interpret your string as a number, so $decision == "Y"
would check whether $decision is 0. If you had use warnings; Perl would have noticed you.
Use $decision eq "Y" instead.
The outer while loop had a trailing ; just after the comparison which will give you an endless
loop or a no-op (depending on the content of $decision).
You forgot the = in $num = $num - 1;.
You forgot the quotes " around print "$fact\n";
system("pause") only works on Windows where pause is an external command. On Linux (where
I just tested) there is no such command and system("pause") fails with command not found.
I replaced it with sleep(5); which simply waits 5 seconds.
.
#!/usr/bin/env perl
use strict;
use warnings;
print "Welcome! Would you like to calculate a factorial? Y/N\n";
my $decision = <STDIN>;
chomp($decision); # remove trailing "\n" from $decision
while ( $decision eq 'Y' ) {
print "Enter a positive # more than 0: \n";
my $num = <STDIN>;
chomp($num);
my $fact = 1;
while ( $num > 1 ) {
$fact = $fact * $num;
$num = $num - 1;
}
print "$fact\n";
print "Calculate another factorial? Y/N\n";
$decision = <STDIN>;
chomp($decision);
}
print "ok.\n";
sleep(5); # wait 5 seconds
Always add use warnings and use strict to the beginning of your program.
There are a number of typos in your code that would have been caught by this.
#!/usr/bin/perl
use warnings;
use strict;
print "Welcome! Would you like to calculate a factorial? Enter 'Y' or 'N': ";
my $answer = <STDIN>;
chomp($answer);
while($answer =~ /^[Yy]$/){
my $fact = 1;
print"Enter a positive number greater than 0: ";
my $num = <STDIN>;
chomp($num);
my $number_for_printing = $num;
while($num > 0){
$fact = $fact * $num;
$num--;
}
print "The factorial of $number_for_printing is: $fact\n";
print"Calculate another factorial? Enter 'Y' or 'N': ";
$answer = <STDIN>;
chomp($answer);
}
print "Goodbye!\n";

Perl Temp Converter

Here's my code for Perl, and when I run it, it will display my menu and I can exit with 3. But both 1 and 2 on the menu aren't registering as value input and instead going straight to else and giving an error resulting in an endless loop.
#! c:\Strawberry\perl\bin\Perl.exe
# This is a simple Temperature converter
# that will convert Fahrenheit to Celsius
# and Celsius to Fahrenheit.
use strict;
my $fahr = 0;
my $cel = 0;
my $choice = 0;
my $input = 0;
print "\n";
print "*********************************************\n";
print "*** This is a Temperature Converter ***\n";
print "*********************************************\n";
print "1. Celsius to Fahrenheit.\n";
print "2. Fahrenheit to Celsius. \n";
print "3. Exit\n";
print "*********************************************\n\n";
print "Enter a choice (1-3): ";
my $choice = <STDIN>;
chomp ($choice);
if(&IsNumeric($choice) == 0) {
$choice = 0;
}
while ($choice != 3) {
# Do conversion from C to F
if ($choice == 1) {
print "\nEnter a Temperature: ";
chomp ($cel = "" );
$fahr = ($cel * (9 / 5)) + 32;
# Format to one decimal
$fahr = sprintf("%.1f", $fahr);
print "$cel degrees Celsius = ";
print "$fahr degrees Fahrenheit\n";
}
# Do conversion from F to C
elsif ($choice == 2) {
print "\nEnter a Temperature: ";
chomp ($fahr = "" );
$cel = ($fahr - 32) * 5 / 9;
# Format to one decimal
$cel = sprintf("%.1f", $cel);
print "$fahr degrees Fahrenheit = ";
print "$cel degrees Celsius\n";
}
# Display Error Message
else {
print "\nYou entered and invalid choice please choose a choice from the menu.\n\n";
}
print "\nEnter a Choice (1-3): ";
chomp ($choice = "" );
if(&IsNumeric($choice) == 0) {
$choice = 0;
}
}
# Sub Name: IsNumeric.
# Description: This sub validates the input to check to see if
# the input is a Numeric value
# Example: 100, 1,000, $10.00, and 14.00 are valid inputs.
sub IsNumeric {
my $InputString = shift;
if ($InputString !~ /^[0-9|.|,]*$/) {
return 0;
}
else {
return 1;
}
}
There's a lot of room for improvement in your code, but since this isn't a code review, I'll answer the question you asked and leave it at that. Your pattern of (not) reading input and chomping it is wrong. Instead of this:
print "\nEnter a Temperature: ";
chomp ($cel = "" );
You need to read from STDIN and then chomp without also assigning to an empty string:
$cel = <STDIN>;
chomp($cel);
Or just:
chomp($cel = <STDIN>);
If you had included use warnings at the top of your script, you would have seen the following message or something very similar to it:
Argument "" isn't numeric in numeric ne (!=) at foo.pl line 59,
line 1.
And that would have alerted you that something wasn't being set to the value you thought it was being set to.
Your code never retrieves a temperature. You have
chomp( $cel = "" );
and
chomp ($fahr = "" );
which just assigns the null string to those variables. You need <> in place of ""
Note also that you should never call a Perl subroutine using an ampersand. Wherever you read instructions to do that, it is very out of date. You should also always use warnings 'all' in addition to use strict, and you should avoid using capital letters for local variables
Finally, your regex /^[0-9|.|,]*$/ uses a character class which will match any one of the characters 0 through 9, a comma, a dot or a pipe. I am sure that's not what you meant. Perl won't handle numbers containing comma or pipe characters
After entering an option 1 or 2 you have asked to enter a temperature, but you are not reading this temperature from input.
This chomp ($cel = "" ); and chomp ($fahr = "" );, you didn't read input and choping it. Write it as:
chomp ($cel = <STDIN>);
and
chomp ($fahr = <STDIN>);
And there is no need to declare $choice two times. This code needs lot of improvement.

Perl program to calculate the sum of the numbers that the user enters

My script needs to get a series of numbers input by the user and find the average of them. I would like to use the line 'end-of-file' to show that the user is done inputting code. Any help would be appreciated. Below is what I have so far. I think I am really close, but I am missing something.
Code:
#! /usr/bin/perl
use 5.010;
print "Enter the scores and type end-of-file when done";
chomp(#scores = <STDIN>);
foreach (#scores) {
push_average(total(#scores));
}
sub total {
my $sum;
foreach (#_) {
$sum += $_;
}
sum;
}
sub average {
if (#_ == 0) {return}
my $count = #_;
my $sum = total(#_);
$sum/$count;
}
sub push_average {
my $average = average(#_);
my #list;
push #list, $average;
return #list;
}
You are quite close. Adding use strict; use warnings at the top of every Perl script will alert you of errors that might go unnoticed otherwise.
A few hints:
You forgot the sigil of $sum in the last statement of total. Currently, you return a string "sum" (without strict vars), or possibly call a sub called sum.
You don't need the foreach in the main part, rather do
my #averages = push_average(#scores);
The total is already calculated inside push_average
You probably want to print out the resulting average:
my $avg = $averages[0];
say "The average of these numbers is $avg";
The push_average is silly; you return a new array of one element. You could return that one element just as well.
Suggested script:
use strict; use warnings; use 5.010;
use List::Util qw/sum/; # very useful module
# say is like print, but appends a newline. Available with 5.10+
say "Please enter your numbers, finish with Ctrl+D";
my #nums = <STDIN>;
chomp #nums;
# The // is the defined-or operator
# interpolating undef into a string causes a warning.
# Instead, we give an expressive message:
my $avg = average(#nums) // "undefined";
say "The average was $avg";
sub average { #_ ? sum(#_) / #_ : undef } # return undef value if called without args
reads up to the newline. You've got a few choices here. You can ask the user to input the numbers separated by spaces and then split it into your #choices array. Or you can keep asking them to enter a number or just hit enter to finish.
Answer 1)
print "Enter scores separated by a space and press enter when done";
chomp($input = <STDIN>);
#choices = split(' ', $input);
Answer 2)
#chomp = ();
do {
print "Enter a score and then press enter. If done, just press enter.";
chomp($temp = <STDIN>);
if($trim ne '') {
push(#choices, $temp);
}
} until ($temp eq '');

Term::ReadKey, "Strg+D" and STDIN

When I type after Enter a string: for example a, b, c and then two times Ctrl+D I get an endless loop which doesn't halt on ReadKey and which I can not stop with the Q key?
#!/usr/bin/env perl
use warnings;
use 5.10.1;
use Term::ReadKey;
while( 1 ) {
my $c;
say "Press the \"q\" key to quit.";
print "Press the \"e\" key to enter a string: ";
{
$|++;
Term::ReadKey::ReadMode 'ultra-raw';
$c = ReadKey 0;
Term::ReadKey::ReadMode 'restore';
}
exit if ord( $c ) == 3; # Control C
last if $c eq 'q';
if ( $c eq 'e' ) {
print "\nEnter a string: ";
my $string = <>;
if ( not defined $string ) {
say "Undefined";
}
else {
chomp $string;
say "You entered |$string|";
}
}
say "Still running";
}
After you type the two Ctrl-D (EOT), your programm will only receive NUL-bytes. And an infinity of them. Unfortunately you have an unconditional read in an infinite loop. Either you change that (e.g. give the user a lesson if he types something other than q or e and exit if he didn't get it after the third try), or you implement control characters correctly. Your module strips all control characters from the input before you even get it, but it provides the neccessary hooks. I urge you to add Ctrl-C as well (it only works when a line is expected, not when a char is being read).
Also, why not compare the input characters with string equality? $c eq "q" reads nicer.
The only line that will terminate your loop is this one:
last if ord( $c ) == 113;
So, the only way to escape the loop is to enter 'q' when the prompt asks you to enter 'e'.
Presumably you want an appropriately placed last inside your if statement, so that ctrl-d and/or any text will terminate.