Why we can not use "le" in for loop and can use in "if condition" - perl

I've made this program as a beginner. Need to clarify something!
Why I m not able to use "le" in a for loop given below but I'm able to use it in "if condition". What is the reason behind that. ?
print "Type the number upto series should run\n";
my $var;
$var = int<STDIN>;
chomp $var;
my ($i, $t);
my $x = 0;
my $y = 1;
if($var eq 1) {
print "\nYour Series is : $x\n";
} elsif($var eq 2){
print "\nYour Series is : $x $y\n";
} elsif($var ge 2) {
print "Your Series is : $x $y ";
for($i = 1; $i le $var - 2; $i++) {
# Why the condition not working when I'm using "le"
# but it does work when I'm using "<="
$t = $x + $y;
$x = $y;
$y = $t;
print "$t ";
}
print "\n";
} else {
print "Error: Enter a valid postive integer\n";
}

You are free to use le and <= as you like. But you should be aware that they are completely different operators.
Numeric comparision operators are
== != < <= > >= <=>
The equivalent string comparision operators are
eq ne lt le gt ge cmp
Strings and numbers are converted to each other as needed. This means for example that
3 ge 20 # true: 3 is string-greater than 20
11 le 2 # true: 11 is string-less-or-equal than 2
because lexicographic ordering compares character by character. Using the numeric operators when you want to treat the contents of your $variables as numbers is therefore preferable and will yield the correct results.
Note that Perl translates between strings and numbers invisibly. It is advisable to use warnings, so that you get a helpful message when a string can't represent a number (e.g. "a").

The correct answer has been given, that ge does string comparison, where for example 11 is considered less than 2. The solution is to use numerical comparison, == and >=.
I thought I would help demonstrate the problem you are having. Consider this demonstration of how the default sort works:
$ perl -le 'print for sort 1 .. 10'
1
10
2
3
4
5
6
7
8
9
As you can see, it considers 10 lower than 2, and that is because of string comparison, which is the default mode for sort. This is how the default sorting routine looks under the hood:
sort { $a cmp $b }
cmp belongs to the string comparison operators, like eq, le, and ge. These operators are described here (cmp is below that).
For the sort routine to do what we expect in the above example, we would have to use a numeric comparison operator, which is the "spaceship operator" <=>:
sort { $a <=> $b }
In your case, you can try out your problem with this one-liner:
$ perl -nlwe 'print $_ ge 2 ? "Greater or equal" : "Lesser"'
1
Lesser
2
Greater or equal
3
Greater or equal
11
Lesser

When you compare numbers with eq, le, gt..... etc; they first be converted to string. And strings will be checkd for alphabatical order, so "11" will be less then "2" here.
So you should be using ==,<=,>= ...etc when comparing numbers.

I thought you may like to see a more Perl-like program that produces the Fibonnaci series like yours.
use strict;
use warnings;
print "Type the length of the series\n";
chomp(my $length = <>);
unless ($length and $length !~ /\D/ and $length > 0) {
print "Error: Enter a positive integer\n";
}
print "\n";
my #series = (0, 1);
while (#series < $length) {
push #series, $series[-2] + $series[-1];
}
print "#series[0..$length-1]\n";

Related

Count Characters in Perl

I need to count the letter "e" in the string
$x="012ei ke ek ek ";
So far, I've tried with a for-loop:
$l=length($x);
$a=0;
for($i=0;$i<$l;$i++)
{$s=substr($x,$i,1);
if($s=="e")
{$a++;}
print $a;
Your code has some problems. You forgot to close the for loop brace,
and in Perl == is supposed to compare numbers. Use eq for strings.
It is also recommended that you use warnings and enable strict mode,
which would have helped you debugging this. In your case, since e
would be treated as 0, so the other one char substrings, 1 and 2
would be the only characters not equal to e when compared with ==. A
cleaned up version of your code could be written as:
use warnings;
use strict;
my $x = "012ei ke ek ek ";
my $l = length $x;
my $count = 0;
for(my $i = 0; $i < $l; $i++) {
my $s = substr($x, $i, 1);
$count++ if ($s eq "e");
}
print $count;
There are multiple ways to achieve this. You could use a match with a
group, which if global returns all the occurrences in list context.
Since you want the number, take this result in scalar context. You can
achieve this for example with:
my $count = () = $string =~ /(e)/g;
Or:
my $count = #{[ $string =~ /(e)/g ]}
Another way is to split the string into characters and grep those that
are e:
my $count = grep $_ eq 'e', split //, $string;
And probably the most compact is to use tr which returns the count of
characters in scalar context, although this does restrict this usage to
counting characters only:
my $count = $string =~ tr/e//;
You compare characters with the numeric operator (==) when you should use the string comparison eq. If you had used the warnings pragma you would have seen that.
You code should have looked like:
#!/usr/bin/env perl
use strict;
use warnings;
my $x = "012ei ke ek ek ";
my $l = length($x);
my $a = 0;
for ( my $i = 0; $i < $l; $i++ ) {
my $s = substr( $x, $i, 1 );
if ( $s eq "e" ) {
$a++;
}
}
print "$a\n";
Proper indentation and the use of the strict and warnings pragmas will avoid and/or catch unintentional, dumb errors.
A much more Perl-ish (and shorter) way to achieve your answer is:
perl -le '$x="012ei ke ek ek";#count=$x=~m/e/g;print scalar #count'
4
This matches globally and collects all the matches in list context. The scalar value of the list gives the number of occurrences you seek.
Another way is to use tr
perl -le '$x="012ei ke ek ek";print scalar $x=~tr/e//'
4
#sidyll Already mentioned what is the problem in your script and all of the possible ways, but TIMTOWTDI.
$x="012ei ke ek ek ";
my $count;
$count++ while($x=~/e/g);
print $count;

Compare 2 sets of numbers contained in strings

I have 2 scalars as below:
$a = '100 105 010';
$b = '010 105 100';
How do I compare if both has same set of values? order doesn't matter.
one way is to arrange it in ascending order and compare, is there a better way?
You can split each string into an array and sort and compare arrays. By hand:
use warnings;
use strict;
my $x = '100 105 1 010';
my $y = '010 105 100 2';
my #xs = sort { $a <=> $b } split ' ', $x;
my #ys = sort { $a <=> $b } split ' ', $y;
if (#xs != #ys) {
print "Differ in number of elements.\n";
}
else {
for (0..$#xs) {
print "Differ: $xs[$_] vs $ys[$_]\n" if $xs[$_] != $ys[$_];
}
}
# For boolean comparison put it in a sub
print arr_num_eq(\#xs, \#ys), "\n";
sub arr_num_eq {
my ($ra, $rb) = #_;
return 0 if #$ra != #$rb;
$ra->[$_] != $rb->[$_] && return 0 for 0..$#$ra;
return 1;
}
The sorting can be moved to the sub as well, which would then take strings. The way it stands it can be used for comparison of existing arrays as well. Please add argument checking.
There is a number of modules that have this capability. The perm from Array::Compare hides the sorting above, but internally joins sorted arrays into strings thus duplicating the work here since we started with strings. The List::AllUtils certainly offers this as well with its long list of utilities.
See this post, for example, for a few methods (just not the smart match ~~), and for benchmarks if efficiency is a concern.
Using the mentioned implementation idea from Array::Compare, per comment by ysth
sub str_num_eq {
return join(' ', sort { $a <=> $b } split / /, $_[0])
eq join(' ', sort { $a <=> $b } split / /, $_[1])
}
What the most suitable method is depends on what this is for and how it is used. Is it only a boolean comparison, or will more be done if they are found to differ? How does it come about in your program flow? What are typical sizes of strings, how often is it run? Are the strings most often the same or different, do they typically differ a lot or a little? Etc.
Without modules, you can use hashes:
#!/usr/bin/perl
use warnings;
use strict;
my $x = '100 105 010 2';
my $y = '010 105 100 100 1';
my (%hx, %hy);
$hx{$_}++ for split ' ', $x;
$hy{$_}++ for split ' ', $y;
for my $k (keys %hx) {
if (! exists $hy{$k}) {
print "$k missing in y\n";
} elsif ($hy{$k} != $hx{$k}) {
print "$k has different number of occurences\n";
}
delete $hy{$k};
}
print "$_ missing in x\n" for keys %hy;
$a and $b are special variables used in sort, so I renamed them to $x and $y.
split turns the strings into lists. Each hash counts how many times a member occurs in the list.
See also Perl FAQ 4.
Something else try with pattern matching,
This is not a straight forward but it will work.
Construct the pattern by anyone of your scalar value. Then check the another string by the constructed pattern.
my $a = '100 100 105';
my $b = '100 105 100';
my #b_ary = split(" ",$b);
my $regex = join'\b|\b', #b_ary;
my $word_length = #b_ary * 2 - 1; #Count the number of words and space.
my $rgx = qr"^(?:\b$regex\b|\s){$word_length}$"; #`{n}` match word exact n times
if($a=~m/$rgx/)
{
print "same values\n";
}
else
{
print "Not a same values\n";
}
The answer is already posted above. This is just in case you want to remove the white spaces and compare each number.
$x = '100 105 010';
$y = '010 105 100';
join("",sort split "",join("",split " ",$x)) eq join("",sort split "",join("",split " ",$y));

Perl string comparison

I have a basic problem with chain comparison in Perl.
use strict;
use warnings;
my $toto ="a";
if ("a" cmp $toto)
{
print "success";
}
else
{
print "fail\n";
}
my $titi=<STDIN>;
It always says fail.
If I use eq rather than cmp, it works.
But if I do:
my $toto = <STDIN>;
if ("a" eq $toto)
{
print "success";
}
else
{
print "fail\n";
}
my $titi=<STDIN>;
And I use a as input, this prints fail.
What is wrong ?
What is the cmp operator?
$a cmp $b yields the lexicographic relation between $a and $b, which means that it returns three values:
-1 meaning $a is less than $b
0 meaning $a is equal to $b
1 meaning $a is larger than $b
$a = "a";
$b = "a";
print ($a cmp $b);
Outputs 0, meaning they are equal
Reading from STDIN
my $toto = <STDIN>;
When you use something like this to read a single line from STDIN (i.e. reading in scalar context) it will read the entire line, including the linefeed.
This means that $toto is "a\n" and not just "a", which is why your latter use of eq fails.
You can fix the issue by using chomp, which will remove the linefeed character from $toto, like this
snippet.pl
my $toto = <STDIN>;
chomp $toto;
if ( $toto eq "a" ) {
print "Yes!\n";
}
An example run would look like this
$ perl snippet.pl
a<return>
Yes!
cmp is the comparison operator, which is mostly useful for sorting strings. This operator is documented in perldoc perlop.
Binary "cmp" returns -1, 0, or 1 depending on whether the left
argument is stringwise less than, equal to, or greater than the right
argument.
For example:
bash$ perl -e 'print sort { $a cmp $b } qw( y z x); print "\n"'
xyz
Check out perldoc perlop equality operators:
Binary "eq" returns true if the left argument is stringwise equal to the right argument.
Binary "cmp" returns -1, 0, or 1 depending on whether the left argument is stringwise less than, equal to, or greater than the right argument.
Instead of "a" cmp $toto try "a" eq $toto or change your conditional statement to use zero as success
The reason it's not working is because cmp isn't eq. They return different values on equality.
#!/usr/bin/env perl
use strict;
use warnings;
my $string = "fish";
print $string cmp $string;
print $string eq $string;
eq is 1 (true) if they match, and 0 (false) if they don't.
cmp returns -1 if it's before (true), 0 if equal (false) and +1 if after. Specifically - this is used for sorting. (And it's counterpart <=> does the same thing, which perhaps is a bit more obvious it isn't comparing equality).
Net result is - you've accidentally inverted the logic of your program.
print "1 True","\n" if "fish2" cmp "fish3";
print "2 True","\n" if "fish2" cmp "fish2";
print "3 True","\n" if "fish2" cmp "fish1";
You could use not to negate the cmp, but really the answer is - use eq instead.
(Also - don't forget to chomp when you read STDIN).

to match a string and an integer in perl

I have a string of numbers, like "4 2 6 7", and a variable i which is an integer. How can I decide if i is included in the string? The code is in perl...
Use this function:
my $string = "4 2 6 7";
my $i = 4;
if ( $string =~ /\b$i\b/ ) {
print "$string contains $i\n";
}
You can use split to create an array from the string "4 2 6 7", and then use grep to search the array.
$ perl -wle 'if ( grep {$_ eq $i} split(" ", "4 2 6 7") ) {print "matched\n";}'
EDIT:
Or you can use '==' instead of 'eq' as the comparison operator to match numbers instead of strings.
For the fun of it, the ~~ smart match operator:
use 5.012;
my $string = "4 2 6 7";
my #test = split /\s+/, $string;
for( 0 .. 9 ) {
say "$_ is contained in $string" if $_ ~~ #test;
}
A good discussion on the power of the smart match operator is found in perlsyn. It can be a little tricky, since it's not an associative operator, and the rules are deeply rooted in DWIMery rather than consistency. But it's very powerful.
Use this regular expression to match the variable i with a word boundary (assuming your string of numbers have a space after each integer):
/\b$i\b/
Here's a version that does not care about the delimeters or formatting of your string. It just extracts sequences of digits and compares them to the search pattern.
I made it into a sub and a functional program, for convenience.
use warnings;
use strict;
my $string = "4 22 6 7";
my $i = shift; # number you want to search for
print "Checking '$string' for: '$i'\n";
print "Result is: ", (is_in($string, $i) ? "Yes" : "No");
sub is_in {
my ($string, $i) = #_;
while ( $string =~ /(\d+)/g ) {
return 1 if ( $1 == $i );
}
return 0;
}
Example output:
C:\perl>t4.pl 4
Checking '4 22 6 7' for: '4'
Result is: Yes
C:\perl>t4.pl 22
checking '4 22 6 7' for: '22'
Result is: Yes
C:\perl>t4.pl 2
checking '4 22 6 7' for: '2'
Result is: No
You can do it easily with the help of split function.
use warnings;
my $string = "4 2 6 7";
my $i = 6; #use any value of $i
my #x = split / /, $string;
my $count = 0;
foreach (#x)
{
if($_ == $i)
{
print "matched at position $count"; die $!;
}
$count++;
}
print "integer doesn't found in string";
Try it on codepad: http://codepad.org/f5a86c9s

How should I use Perl's scalar range operator?

What is the scalar ".." operator typical usage? Is it only selecting blocks of text?
Interesting example by myself:
sub get_next {
print scalar($$..!$$), "\n";
}
get_next for 1 .. 5; # prints numbers from 1 to 5
get_next for 1 .. 5; # prints numbers from 6 to 10
People hardly seem to know about it based on questions here, but, yes, I'd say typical usage is selecting blocks of text, either with
while (<>) {
print if /BEGIN/ .. /END/;
}
or
while (<>) {
print if 3 .. 5; # prints lines 3 through 5
}
The latter is syntactic sugar for checking against the input line-number ($.)
... if $. == 3 .. $. == 5;
which suggests the weird-looking
#! /usr/bin/perl -l
for ($_ = 1; $_ <= 10; ++$_) {
print if $_ == 4 .. $_ == 7;
}
The above program's output is
4
5
6
7
If you have some sort of bracketing condition, test for it in subs:
for (...) {
do_foo($x,$y,$z) if begin($x) .. end($z);
}
Outside of perl -e you really shouldn't. It is esoteric and funky. See my post not even 24hrs ago about it about how it maintains state with calling context. This stung me in a real world application, because I was trying to be clever and found what I thought was a good use-case for it.
Here's a place where you need to be very careful about unintentional use of the scalar range operator: subroutine returns.
sub range {
my $start = shift;
my $end = shift;
return $start .. $end;
}
#foo = range( 1, 5 ); # ( 1, 2, 3, 4, 5 )
$foo = range( 1, 5 ); # False or maybe true. Who knows.
If you call this subroutine in scalar context, you'll be in for a surprise.
After being bitten by some variant of this problem, I now always make sure I assign a list return into an array, thereby getting array-like context behaviors for my subs. If I need other context specific behavior (very rarely) I use wantarray.
sub range {
my $start = shift;
my $end = shift;
my #result = $start .. $end;
return #result;
}
another use is simple counters like this:
perl -e 'foreach ( 1 .. 100 ){print"$_\n"}'