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.
Related
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;
}
I'm working on a homework assignment that gives me the task to take four user-inputted numbers and compares them to output the largest and the smallest. I can't use loops so I'm using if/else/elsif statements but I keep getting an error. I've got this so far and I'm not sure what I'm doing wrong.
[Shebang]
my $small;
my $big;
print "\nEnter first number: ";
chomp (my $one = <>);
print "\nEnter second number: ";
chomp (my $two = <>);
print "\nEnter third number: ";
chomp (my $three = <>);
print "\nEnter fourth number: ";
chomp (my $four = <>);
if ($one >= $two) {
$one = $big;
$two = $small;
}
else {
$one = $small;
$two = $big;
}
if ($three >= $big) {
$three = $big;
}
elsif ($three <= $small) {
$three = $small;
}
if ($four >= $big) {
$four = $big;
}
elsif ($four <= $small) {
$four = $small;
}
print "LRG: $big\n";
print "SML: $small\n";
Ideally, you type in four numbers and it outputs the largest and smallest. Instead, I get
"Use of uninitialized value $big in numeric ge (>=) at [filename].pl line 29, <> line 4." at line 29 and 36.
I also get
"Use of initialized value $small in concatenation (.) or string line 44, <> line 4."
Perl works the same way as pretty much every other programming language. In an assignment statement, the value on the right-hand side of the operator is assigned to the variable on the left-hand side of the operator. So, in a statement like:
$one = $big;
You assign the value of $big to the variable $one. As has already been pointed out in a comment, this is the wrong way round and you really wanted:
$big = $one;
There are a couple of simpler approaches, you can take. Firstly, you could sort the list of numbers and then take the first and last elements from the list:
my #sorted = sort { $a <=> $b } ($one, $two, $three, $four);
my ($small, $big) = #sorted[0, $#sorted];
Or you can use the min() and max() functions from the module List::Util.
use List::Util qw[min max];
my $small = min($one, $two, $three, $four);
my $big = max($one, $two, $three, $four);
I'm reading Learning Perl (6th edition) and came upon a code snippet that I couldn't decipher. One of the exercises after chapter 14 asks to build a program that takes as input a string and a substring then finds the indices at which the substring occurs in the string.
Here's the way I did it:
print "Enter a string: ";
chomp($string = <STDIN>);
print "Enter a substring: ";
chomp($sub = <STDIN>);
until ($index == -1) {
print $index, "\n" if defined($index);
$index = index($string, $sub, $index + 1);
}
In the answers section, they show two ways. One is easy to understand and similar to mine, but the other is purposefully obfuscating:
print "Enter a string: ";
chomp($string = <STDIN>);
print "Enter a substring: ";
chomp($sub = <STDIN>);
for (my $pos = –1; –1 !=
($pos = index
+$string,
+$sub,
+$pos
+1
);
push #places, ((((+$pos))))) {
'for ($pos != 1; # ;$pos++) {
print "position $pos\n";#;';#' } pop #places;
}
print "Locations of '$sub' in '$string' were: #places\n";
I have almost no idea what's going on in that for loop. I know it's of the form for (initialize; test; increment) and that it's testing that the index is not -1, which means no more occurrences of the substring. But what's going on with the assignment to $pos? Why are there so many parentheses around +$pos? What's going on after the many parentheses? I'd really appreciate it if someone could walk me through the second part. Please keep in mind I've just started learning Perl a week ago.
BTW, I tried running their code but it gave me this error:
Unrecognized character \xE2; marked by <-- HERE after my $pos = <-- HERE near column 16 at ex14.obfs.pl line 1.
I've simplified your example just a little bit, discarded the useless junk and comments. Hope now it is clear what's going on:
print "Please enter a string: ";
chomp(my $string = <STDIN>);
print "Please enter a substring: ";
chomp(my $sub = <STDIN>);
my #places;
for (my $pos = -1; -1 != ($pos = index $string, $sub, $pos+1); push #places, $pos)
{
#do nothing here
}
print "Locations of '$sub' in '$string' were: #places\n";
The compilation error was due to '–' instead of '-';
The inner loop was in fact a string literal (useless) plus comments (useless), the extra braces are useless as well.
For a given value N I am trying to output the corresponding Fibonacci number F(N). My script doesn't seem to enter the recursive stage. fibonnaci($number) is not calling the subroutine. It is simply outputing "fibonacci(whatever number is inputted)".
Here is my code:
#!/usr/bin/perl -w
use warnings;
use strict;
print "Please enter value of N: ";
my $number = <STDIN>;
chomp($number);
sub fibonacci
{
my $f;
if ( $number == 0 ) { # base case
$f = 0;
} elsif ( $number == 1 ) {
$f = 1;
} else { # recursive step
$f = fibonacci( $number - 1 ) + fibonacci( $number - 2 );
}
return $f;
}
print "\nf($number) = fibonacci($number)\n";
Sample Output:
Please enter value of N: 4
f(4) = fibonacci(4)
user1:~>recursiveFib.pl
Please enter value of N: 5
f(5) = fibonacci(5)
user1:~>recursiveFib.pl
Please enter value of N: 10
f(10) = fibonacci(10)
user1:~>
Not sure where I went wrong. Any help would be greatly appreciated.
You need to accept the function arguments properly and take the function call out of the quotes.
use warnings;
use strict;
sub fibonacci {
my ($number) = #_;
if ($number < 2) { # base case
return $number;
}
return fibonacci($number-1) + fibonacci($number-2);
}
print "Please enter value of N: ";
my $number = <STDIN>;
chomp($number);
print "\n$number: ", fibonacci($number), "\n";
A more efficient but still recursive version:
sub fib_r {
my ($n,$a,$b) = #_;
if ($n <= 0) { return $a; }
else { return fib_r($n-1, $b, $a+$b); }
}
sub fib { fib_r($_[0], 0, 1); } # pass initial values of a and b
print fib(10), "\n";
Other answers have already mentioned the lack of taking an argument correctly to the fibonacci function, and that you can't interpolate a function call in the print string like that. Lately my favourite way to interpolate function calls into print strings is to use the ${\ ... } notation for embedding arbitrary expressions into strings:
print "f($number) = ${\ fibonacci($number) }\n";
Other techniques include separate arguments:
print "f($number) = ", fibonacci($number), "\n";
or a helper variable:
my $result = fibonacci($number);
print "f($number) = $result\n";
or even printf:
printf "f(%d) = %d\n", $number, fibonacci($number);
Of all these techniques I tend to prefer either of the first two, because they lead to putting the expressions "in-line" with the rest of the text string, whereas in the latter two they sit elsewhere, making it harder to see at a glance what gets printed where. Especially with printf's positional arguments, it can be easy to be "off-by-one" with a large number of arguments, and put everything in the wrong place.
You are printing in wrong way. you just need to handle the return value. Also the way you are using Number in the sub is also not seems relevant. I have updated the and its working fine.
Also the values that you wanted to print is depend on the start up of the series. whether you want to start from 0 or 1.
The series example start with 1 is 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, so if you put 10 you will get 55.
#!/usr/bin/perl -w
use warnings;
use strict;
print "Please enter value of N: ";
my $number = <STDIN>;
chomp($number);
my $result=fibonacci($number);
sub fibonacci
{
my $f =0;
if ($_[0] == 1 ) { # base case
$f = 1;
} elsif ( $_[0] == 2 ) {
$f = 1;
} else { # recursive step
$f= fibonacci( $_[0] - 1 ) + fibonacci( $_[0] - 2 );
}
return $f;
}
print "\nf($number) = $result\n";
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.