non-numeric argument from user input - perl

In this code the user is asked to input a number from the keyboard. The variable is then used again at a later point in division.
When the code is run it receives a
360/(whatever number was typed) isn't numeric in addition.
Is Perl interpreting the user input as a string instead of a numeric value?
use strict;
use warnings;
use Language::Logo;
print "Enter a number: ";
chomp(my $number = <STDIN>);
my $lo = new Logo(title => "Logo Demonstration");
$lo->command("update 2; color random; pendown; hideturtle");
for ( my $i = 1; $i < 999; $i++ ) {
my $distance = $i / 4;
$lo->command("forward $distance; right 360/$number");
}
$lo->disconnect("Press ctrl+c to exit");

What if you try:
$lo->command("forward $distance; right " . (360/$number));
If that doesn't work, try to force Perl to "see" $number as a number by adding 0 to it:
$rotation = 360 / ($number+0);
$command = "forward $distance; right " . $rotation;
$lo->command($command);
And if that doesn't work either, try to manually set $command to something that DOES work, and report back.

Related

Storing variables dynamically in Perl

I am trying to store and print variables dynamically in Perl, by asking user to input number of variables to create, then asking for each of the created variables to add information then output the length of text contained in each of them. In my head I came up with this:
use strict;
use warnings;
sub main {
my %VarStore = ();
print ("How many variables to create: ");
chomp(my $varNum = <STDIN>);
my $counter = 1
while ($counter <= $varNum) {
print "Enter text to variable $counter: \n";
chomp(my $buffer = <STDIN>);
$VarStore{'var'$counter} = $buffer;
$counter ++;
}
while ($counter <= $varNum) {
print "Variable $counter is length($VarStore{'var'$counter}) character long \n";
$counter ++;
}
}
What I would like is:
> How many variables to create: 3
> Enter text to variable 1: ABCQWEPOL
> Enter text to variable 2: xJSAG!HHKSKASK
> Enter text to variable 3: KakA
> Variable 1 is 9 character long
> Variable 2 is 14 character long
> Variable 3 is 4 character long
Any clue why my code is not working? I thought of a hash here so that I can create dynamic variables say with keys var1, var2, var3, etc depending on the input the user gives to create them. Thanks in advance.
You are correct that a hash is a good solution to this problem. You have two problems in your code. First, $VarStore{'var'$counter} is not valid syntax, you need to use the . operator to concatenate strings $VarStore{'var'.$counter}, or you can use double quotes to interpolate variables into strings $VarStore{"var$counter"}.
Unlike variables, you can't directly interpolate function calls into strings, so the length() call should be done separately. Or alternatively you can concatenate strings with the function call. print "Variable $counter is " . length($VarStore{"var$counter"}). " long\n";
Second problem is that after your first while loop completes, the $counter variable you reuse for the next while loop will already be greater than $varNum, so you need to reset it to 1. $counter = 1;
It may be simpler to use foreach loops to iterate through the count. Also, sub main is not needed but if you use it you need to actually call main(); somewhere so it will run.
use strict;
use warnings;
my %VarStore;
print ("How many variables to create: ");
chomp(my $varNum = <STDIN>);
foreach my $counter (1..$varNum) {
print "Enter text to variable $counter: \n";
chomp(my $buffer = <STDIN>);
$VarStore{"var$counter"} = $buffer;
}
foreach my $counter (1..$varNum) {
my $length = length($VarStore{"var$counter"});
print "Variable $counter is $length character long \n";
}

Scoping in Perl

As a biology student, I'm trying to extend my programming knowledge and I ran into a problem with Perl.
I'm trying to create a program that generates random DNA strings and performs analysis work on the generated data.
In the first part of the program, I am able to print out the strings stored in the array, but the second part I cannot retrieve all but one of the elements of the array.
Could this be part of the scoping rules of Perl?
#!usr/bin/perl
# generate a random DNA strings and print it to file specified by the user.
$largearray[0] = 0;
print "How many nucleotides for the string?\n";
$n = <>;
$mylong = $n;
print "how many strings?\n";
$numstrings = <>;
# #largearray =();
$j = 0;
while ( $j < $numstrings ) {
$numstring = ''; # start with the empty string;
$dnastring = '';
$i = 0;
while ( $i < $n ) {
$numstring = int( rand( 4 ) ) . $numstring; # generate a new random integer
# between 0 and 3, and concatenate
# it with the existing $numstring,
# assigning the result to $numstring.
$i++; # increase the value of $i by one.
}
$dnastring = $numstring;
$dnastring =~ tr/0123/actg/; # translate the numbers to DNA characters.
#print $dnastring;
#print "\n";
$largearray[j] = $dnastring; #append generated string to end of array
#print $largearray[j];
#print $j;
#IN HERE THERE ARE GOOD ARRAY VALUES
#print "\n";
$j++;
}
# ii will be used to continuously take the next couple of strings from largearray
# for LCS matching.
$mytotal = 0;
$ii = 0;
while ( $ii < $numstrings ) {
$line = $largearray[ii];
print $largearray[ii]; #CANNOT RETRIEVE ARRAY VALUES
print "\n";
$ii++;
#string1 = split( //, $line );
$line = $largearray[ii];
#print $largearray[ii];
#print "\n";
$ii++;
chomp $line;
#string2 = split( //, $line );
$n = #string1; #assigning a list to a scalar just assigns the
#number of elements in the list to the scalar.
$m = #string2;
$v = 1;
$Cm = 0;
$Im = 0;
$V[0][0] = 0; # Assign the 0,0 entry of the V matrix
for ( $i = 1; $i <= $n; $i++ ) { # Assign the column 0 values and print
# String 1 See section 5.2 of Johnson
# for loops
$V[$i][0] = -$Im * $i;
}
for ( $j = 1; $j <= $m; $j++ ) { # Assign the row 0 values and print String 2
$V[0][$j] = -$Im * $j;
}
for ( $i = 1; $i <= $n; $i++ ) { # follow the recurrences to fill in the V matrix.
for ( $j = 1; $j <= $m; $j++ ) {
# print OUT "$string1[$i-1], $string2[$j-1]\n"; # This is here for debugging purposes.
if ( $string1[ $i - 1 ] eq $string2[ $j - 1 ] ) {
$t = 1 * $v;
}
else {
$t = -1 * $Cm;
}
$max = $V[ $i - 1 ][ $j - 1 ] + $t;
# print OUT "For $i, $j, t is $t \n"; # Another debugging line.
if ( $max < $V[$i][ $j - 1 ] - 1 * $Im ) {
$max = $V[$i][ $j - 1 ] - 1 * $Im;
}
if ( $V[ $i - 1 ][$j] - 1 * $Im > $max ) {
$max = $V[ $i - 1 ][$j] - 1 * $Im;
}
$V[$i][$j] = $max;
}
} #outer for loop
print $V[$n][$m];
$mytotal += $V[$n][$m]; # append current result to the grand total
print "\n";
} # end while loop
print "the average LCS value for length ", $mylong, " strings is: ";
print $mytotal/ $numstrings;
This isn't a scoping issue. You have declared none of your variables, which has the effect of implicitly making them all global and accessible everywhere in your code
I reformatted your Perl program so that I could read it, and then added this to the top of your program
use strict;
use warnings 'all';
which are essential in every Perl program you write
Then I added
no strict 'vars';
which is a very bad idea, and lets you get away without declaring any variables
The result is this
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 60.
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 61.
Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 67.
Argument "j" isn't numeric in array element at E:\Perl\source\dna.pl line 42.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 60.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 61.
Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 67.
Bareword "j" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 42.
Execution of E:\Perl\source\dna.pl aborted due to compilation errors.
Line 42 (of my reformatted version) is
$largearray[j] = $dnastring
and lines 60, 61 and 67 are
$line = $largearray[ii];
print $largearray[ii]; #CANNOT RETRIEVE ARRAY VALUES
and
$line = $largearray[ii];
You are using j and ii as array indexes. Those are Perl subroutine calls, not variables. Adding use strict would have stopped this from compiling unless you had also declared sub ii and sub j
You might get away with it if you just change j and ii to $j and $ii, but you are certain to get into further problems
Please make the same changes to your own code, and declare every variable that you need using my as close as possible to the first place they are used
You should also improve your variable naming. Things like #largearray are pointless: the # says that it's an array, and whether it's large or not is relative, and of little use in understanding your code. If you have no better description of its purpose then #table or #data are probably a little better
Likewise, please avoid capital letters and most single-letter names. #V, $Cm and $Im are meaningless, and you would need fewer comments if those names were better
You certainly wouldn't need comments like # end while loop and # outer for loop if you had indented your blocks properly and kept them short enough so that both the beginning and the end can be seen on the screen at the same time, and the fewer comments you can get away with the better, because they badly clutter the code structure
Finally, it's worth noting that the C-style for loop is rarely the best choice in Perl. Your
for ( $i = 1; $i <= $n; $i++ ) { ... }
is much clearer as
for my $i ( 1 .. $n ) { ... }
and declaring the control variable at that point makes it unnecessary to invent new names like $ii for each new loop
I think you have a typo in your code:
ii => must be $ii
don't forget to put this at the beginning of your code:
use strict;
use warnings;
in order to avoid this (and others) kind of errors

Use of inintialized value $ in concatenation (.) Perl

use warnings;
$a = 5;
$c = 3;
$i;
$x = time();
$m = 32;
$caracter_aleatorio;
#caracteres = (A..Z);
print "Ingrese la cantidad de letras que desea generar=> ";
$n = <STDIN>;
sub generadorMultiplicativo{
$numAleatorio = ((($a*$x) + $c) % $m);
$x = $numAleatorio;
}
for($i=1;$i<=$n;$i++){
&generadorMultiplicativo();
$caracter_aleatorio = $caracteres[$numAleatorio];
if($numAleatorio == 0){
$numAleatorio++;
}
if($numAleatorio > 26){
$numAleatorio = $numAleatorio - 5;
}
print"Letra #$i = $caracter_aleatorio\n";
}
<>;
I know this warning is when the variable doesn't have a value, but I´ve tried everything but it´s the same, it´s a generator of "random" letters.
Could you help me?
Always use strict when writing new code.
Uninitialized warnings means that your $caracter_aleatorio variable is undefined, so you should set a default. Undefined variables are usually a sign of buggy code.
Declare your variables using my
One-letter variables should be avoided for maintenance / readability reasons.
No-longer need to call functions with &foo() notation, foo() is fine.
Use quotes around A and Z to generate the range.
Here is an updated version of your code:
use strict;
use warnings;
my $a = 5;
my $c = 3;
my $i;
my $x = time();
my $m = 32;
my $caracter_aleatorio;
my $numAleatorio;
my #caracteres = ('A'..'Z');
print "Ingrese la cantidad de letras que desea generar=> ";
my $n = <STDIN>;
sub generadorMultiplicativo{
$numAleatorio = ((($a*$x) + $c) % $m);
$x = $numAleatorio;
}
for( $i=1; $i<=$n; $i++ ) {
generadorMultiplicativo();
$caracter_aleatorio = $caracteres[$numAleatorio] || 'unknown';
if($numAleatorio == 0){
$numAleatorio++;
}
if($numAleatorio > 26){
$numAleatorio = $numAleatorio - 5;
}
print"Letra #$i = $caracter_aleatorio\n";
}
Suggestions to improve further:
Learn how to accept/return parameters from subroutines. perldoc perlsub
Input validation, what if I pass in 'abc' instead of 123, numeric vs string comparison operators ( <= vs lt, == vs eq, >= vs gt, etc. ).
Write comments to show high level intent of your code.
Provide a usage/help message to show a new user how to use your script.
The perl documentation is excellent, have a read through perldoc perlintro
To see the issue, after you call generadorMultiplicativo(); you can do some debug printing of $numAleatorio to see that it sometimes goes higher than you intend. That is, it can be greater than 25.
print "DEBUG: $numAleatorio of $#caracteres\n";
Your method tries to fix these out of bounds values, but you have some issues:
26 as the upper limit instead of 25
You subtract 5 if above upper bound but sometimes you are more than 5 above upper bound.
Your pseudorandom number generator is generating values too high.
Recommended rewrite:
use strict;
use warnings;
my #caracteres = ('A'..'Z');
print "Ingres la cantidad de letras que desea generar=> ";
while(my $n = <STDIN>) {
last unless $n =~ /^[0-9]+$/; # Check upper limit?
for (1.. $n) {
print "Letras #%d = %s\n", $_, $caracteres[int(rand(26))];
}
}
Does almost the same thing except you can keep asking for more random chars. Just enter non-digit to exit.
First post/answer and typed on my phone so please forgive my mistakes!

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.

Looking for a perl scripts to make addition and pick last number

I am trying to write a simple perl script to learn Perl. This is the first script I have written using user input. The script needs to get a last numbers after make addition function Any help would be appreciated. Below is what I have so far.
Example user input 9423 and then the scripts were make addition function like below
09+04=13
04+02=06
02+03=05
03+09=12
print "Enter 4 Digits Number";#9423
chomp($number = <STDIN>);
EDIT
How to pick a last 2 digits numbers so the results are 3652
#!/usr/bin/perl
my #nums = ("9423" =~ /(\d{1})/g);
my $a = $nums[0];
my $b = $nums[1];
my $c = $nums[2];
my $d = $nums[3];
my $ab= $a+$b;
my $bc= $b+$c;
my $cd= $c+$d;
my $da= $d+$a;
printf "%02d\n%02d\n%02d\n%02d\n", $ab, $bc, $cd, $da;
I think, here code will output your expected result
#!/usr/bin/perl
use warnings;
use strict;
print "Enter 4 Digits Number:";#9423
chomp(my $number = <STDIN>);
my #digits = split("", $number);
my #lasts;
# add first digit to the last position
#digits = 94239
$digits[$#digits + 1] = $digits[0];
for(my $i = 0; $i < $#digits; $i++){
$lasts[$i] = ($digits[$i] + $digits[$i + 1]) % 10;
}
print join "",#lasts,"\n";
output after enter number 9423:
3652
probably easiest way to do it is use a for loop and substring
EDIT (now tested):
for my $i (0 .. length($number)-2) {
print substr($number, $i, 1) + substr($number, $i+1, 1);
}
Perhaps the following will be helpful:
use strict;
use warnings;
print "Enter a 4 Digit Number: ";
chomp( my $num = <STDIN> );
my #nums = split //, $num;
$nums[ $#nums + 1 ] = $nums[0];
( $nums[$_] + $nums[ $_ + 1 ] ) =~ /(.)$/ and print $1 for 0 .. $#nums - 1;
Output after entering 9423:
3652