Matching inside square Brackets in Perl - perl

When I try to match 2 variables which are same, it works until there is a square bracket in perl for me.
For ex, VAR1 = u6701, VAR2 = u6701 matches and gives me EQUAL
However, VAR1 = aw[101], VAR2 = aw[101] gives me UNEQUAL.
I use $VAR1 == $VAR2 to check and both the variables are strings. Please help.
Thanks.

== is the numeric equality operator in Perl, it checks that two things are equal as numbers. eq is the string equality operator, that's what you want to be using. "1" and "01.00" are equal as numbers but not as strings. Here's the docs on all the equality operators. There is also the pretty good online book Beginning Perl.
Why == sometimes works is because Perl is pretty liberal, to the point of desperation, about interpreting strings as numbers. Often it will simply consider a string to be 0, but sometimes it will find a number in the string and use it. For example, "101aw" will be interpreted as 101, but "aw101" is 0. Do not rely on this.
BTW Perl will warn you about all this, but not by default. You have to turn on strict and warnings and I highly recommend you do and deal with all the issues it brings up. It will save you (and us) lots of time.

In perl in order to check two string values I recommend you to use eq
for eg
if($VAR1 eq $VAR2)
return true;
else
return false;
== tests equality for numbers.
eq does the same for strings.
You can also use the cmp operator, which is the non-numerical equivalent of the <=> operator:
$result = $string1 cmp $string2;
$result will be:
`0` if the strings are equal
`1` if string1 is greater than string2
`-1` if string1 is less than string2

Related

How do I compare two objects in Perl without coercing them to the same type?

In Perl, I know of three ways to test objects for equality: ==, eq, and ~~. All of these tell me that 1 is equal to "1" in Perl 5.
However, 1 and "1" are not the same thing. How can I compare two objects so that 1 equals 1, "1" equals "1", 1 does not equal "1" or 2, and "1" does not equal "01"? Answers for both Perls would be appreciated.
Don't. In Perl, one is one. Polymorphism based on a value's type is bound to fail. That's why Perl has two comparison operators, and that's why ~~ is broken[1].
For example, the scalar returned by !0 contains three values, one stored as an integer, one stored as a floating point number, and one stored as a string.
For example, an object of class package Foo; use overload '0+' => sub { 1 }, fallback => 1; might not contain one at all, but it's considered to be one in numerical and string contexts.
That's why it's still flagged as experimental.
Serializers like Data::Dumper::Dumper and JSON::encode_json can treat scalars internally stored as numbers differently from scalars internally stored as strings, so you can compare serialized output:
use Data::Dumper;
$x = 1;
$y = "1";
$same = Dumper($x) eq Dumper($y);
You can use this snippet as a starting point for your comparison function:
my $num = 42;
my $str = "42";
say B::svref_2object(\$num)->FLAGS;
say B::svref_2object(\$str)->FLAGS;
You will see that the flags of both variables differ. Read perldoc B for more in-depth information.
You may also find the source code of JSON::PP interesting. Search for "sub value_to_json".

What is the definition of stringwise equality in Perl?

I was curious about the results of the following perl snippets:
my $var1 ;
my $var2 ;
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
and
my $var1 ;
my $var2 = "";
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
They turn out to be yes(Perl 5.16).
Unlike javascript specificaton, there is clear description of Equality Comparison Algorithm(http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3), the perl doc for Equality Operators says:
Binary "eq" returns true if the left argument is stringwise equal to the right argument.
But what is the definition of stringwise equality?
You're not having a problem with the definition of stringwise equality. What you don't seem to have wrapped your head arround yet is the concept of stringification. In this particular case, undef stringifies to ''.
Perl has monomorphic operators (mostly) and contextually polymorphic data types.
What this means is that it is the operator that dictates how the data is represented. The eq operator is for stringwise equality. The == operator is for numeric equality. When == is applied to strings, they are treated as numbers. When eq is applied to numbers, they are treated as strings. Each of these internal transformations follow a very specific set of rules.
Thus, when you apply a stringwise operator to a pair of values, those values will be treated as strings. When you apply a numeric operator to a pair of values, they will be treated as numbers.
In your second example, $var1 contains undef, and $var2 contains an empty string. When you compare $var1 eq $var2, the rules of stringification are applied to the operands. $var2 is already a string. But $var1 must be stringified before it can be compared. The rule of stringification for an undefined value is to treat it as an empty string. So $var1 eq $var2 is seen by the eq operator as '' eq '', which is to say, empty string eq empty string.
TRUE.
Note: In modern versions of Perl, using an undefined scalar variable in a stringwise operation only results in stringification for the duration of the operation; the underlying data in the container isn't altered. (see perldata).
perldata is one resource on this topic.
Your definition question has been answered by DavidO, but the (probably) most important piece of information is in TLP's comment - you should always use these two at the top of every script:
use strict;
use warnings;
The one relevant in this case is use warnings, which will produce the following warning:
Use of uninitialized value $var1 in string eq
Some programs go even further - this will cause the program to die because of this:
use warnings FATAL => 'all';
You are comparing variables using eq, which means you are doing a string comparison. A string comparison expects two strings, but you are providing an undefined variable (variable declared, but not defined, so not a string). This is not clean because you are using that operator with invalid input. If you wanted to know if the variables differ, not just the strings they (may or may not) represent, you would not use a string comparison like that.
It works because Perl knows that you want to compare strings (eq) so it assumes you don't care about the fact that a variable has no value assigned. The undefined variable is converted into an empty string (temporarily), which is then compared. (Actually, the variable itself isn't converted, it's still undef after the comparison, but that's not important.)
Of course, this assumption could be wrong and there might be a bug in your code. So it'd be better to check for invalid input before comparing it.
You don't care about the difference between undef and '' (and you know why).
You could explicitly compare empty strings instead of undef. Another programmer who is reading your code will know what's going on (won't assume there's a bug).
if (($var1 // q()) eq ($var2 // q()))
...
In many cases, you might actually care about undef.
For example, your script might take some input (maybe a hash) and if that input variable is an empty string, that would be okay, but if it's undef (maybe not found in the input hash), that would be an error.
if (!defined($var1))
{
die "Input data missing, can't continue!";
}
if ($var1 eq $var2)
...

Perl thinks all strings are equal

Edit: I did try using eq instead of == earlier, and it did not work. For some reason now it does. It's possible that there was another error at that point which prevented it from working, but now this has been resolved. Thank you.
I'm trying to do some simple validation. I want to make sure the redirect url being fed through a variable begins with a particular site or not. If it does, the redirect goes through, if not, it redirects to the root of the site. Seems pretty straight forward, right?
$redir = $input{'redirect'};
$redir_sub = substr($redir, 0, 21);
if ($redir_sub == "http://www.mysite.com") {
print "Location: $redir \n\n";
}else{
print "Location: http://www.mysite.com \n\n";
}
The thing is, no matter what variable I place in there, it the "if" returns as true. I could put my favorite webcomic in there and it'll redirect to it, despite the string not matching. For example this:
$redir = $input{'redirect'};
$redir_sub = "http://www.yahoo.com"
if ($redir_sub == "http://www.mysite.com") {
print "Location: $redir_sub \n\n";
}else{
print "Location: http://www.mysite.com \n\n";
}
That redirects to yahoo! What is going on?
if ($redir_sub == "http://www.mysite.com")
should be
if ($redir_sub eq "http://www.mysite.com")
as eq is string equality operator, and using == forces number comparison so in this case condition always evaluates to trues as 0 == 0 is true.
Operator == is used to compare numbers. You should replace it with operator eq
TL;DR: use eq not ==!
Perl as a language seems to have a problem: It uses the same data type (the scalar) for a lot of different things, including strings and numbers. A scalar is both at the same time, not just one of those. Perl has no type annotations, and there is no way to indicate if a variable holds a string or a number.
This produces problems when we consider equality tests. Assuming two scalars $a = "42.0" and $b = 42. Are they equal? Yes and no:
The strings "42" and "42.0" are not the same thing! These are not equal.
The numbers 42 and 42.0 are equal!
As indicated above, Perl does not use a type system to solve this ambiguity. Rather, it uses different sets of operators for string and numeric operations:
eq ne lt le gt ge cmp
== != < <= > >= <=>
Your problem is that you are not using
use warnings;
Which is why Perl is allowing you to make this mistake. You are using the numeric equality operator to compare strings. Hence, Perl first tries to convert each parameter to a number. And since your strings do not begin with numbers (and do not look like numbers), they are converted to zero 0. Hence your expression
if ($redir_sub == "http://www.mysite.com")
Really means this
if (0 == 0)
Which of course always returns true.
If you had been using warnings, you would have gotten the errors:
Argument "http..." isn't numeric in numeric eq (==) at ...
Argument "http..." isn't numeric in numeric eq (==) at ...
Which would have been a hint as to your problem that you should be using eq and not == to compare strings.
== does numeric comparison. eq does string comparison. When you use a string as a number, perl passes your string through your c library's aton(). So you're really asking your computer if 0 == 0 which is true.

Conditional Statement giving seemingly wrong answer: What am I missing

So I have to make a simple calculator in Perl that maintains an accumulator and does simple operations. The accumulator starts at 0 and then changes based on the results that I receive. At the moment I am only trying to get addition to work. When I check to ensure that the operator entered is + something goes wrong. For instance:
Accumulator: 0
Operator: Anything put here results in addition. Including this sentence.
Operand: 4
Accumulator: 4
It catches numbers but nothing else. I have tried using grep and a list of the operators. I have exhausted all of my ideas. Here is my code (Fyi first post so help me with any noob errors):
my $running = 1;
my $accum = "0";
my $operator;
my $operand;
print("Welcome to the simple, command line calculator.\n");
print("To terminate, press Control-C.\n\n");
while ($running){
print("\nAccumulator: ".$accum."\n");
print("Operator: ");
$operator = <STDIN>;
if ($operator == "+"){
print("Operand: ");
operand = <STDIN>;
$accum += $operand;
}
else{
print("Invalid operator: ".$operator."\n");
}
}
Perl doesn't remove the ending newline from input unless you use the -l option, so you're comparing "+" against "+\n". Usually you want to do soemthing like chomp($operator);.
That said, your real problem is that == does numeric comparison, and both "+" and "+\n" evaluate to 0 in numeric context. (Using -w, as you should always do, would warn you about this.) Use the eq operator for string comparison.
== compares numbers, not strings. If you compare strings, the strings will be converted to numbers; for non-numeric strings, this means they become 0. So $operator == "+" becomes 0 == 0.
For strings, use eq instead. Additionally, keep in mind that <STDIN> will preserve newlines; make sure to chomp $operator as well.

Autoincrementing letters in Perl

I do not understand autoincrementing letters in Perl.
This example seems perfectly understandable:
$a = 'bz'; ++$a;
ca #output
b gets incremented to c. There is nothing left for z to go to, so it goes back to a (or at least this is how I see the process).
But then I come across statements like this:
$a = 'Zz'; ++$a;
AAa #output
and:
$a = '9z'; ++$a;
10 #output
Why doesn't incrementing Zz return Aa? And why doesn't incrementing 9z return 0z?
Thanks!
To quote perlop:
If, however, the variable has been
used in only string contexts since it
was set, and has a value that is not
the empty string and matches the
pattern /^[a-zA-Z]*[0-9]*\z/, the
increment is done as a string,
preserving each character within its
range, with carry.
The ranges are 0-9, A-Z, and a-z. When a new character is needed, it is taken from the range of the first character. Each range is independent; characters never leave the range they started in.
9z does not match the pattern, so it gets a numeric increment. (It probably ought to give an "Argument isn't numeric" warning, but it doesn't in Perl 5.10.1.) Digits are allowed only after all the letters (if any), never before them.
Note that an all-digit string does match the pattern, and does receive a string increment (if it's never been used in a numeric context). However, the result of a string increment on such a string is identical to a numeric increment, except that it has infinite precision and leading zeros (if any) are preserved. (So you can only tell the difference when the number of digits exceeds what an IV or NV can store, or it has leading zeros.)
I don't see why you think Zz should become Aa (unless you're thinking of modular arithmetic, but this isn't). It becomes AAa through this process:
Incrementing z wraps around to a. Increment the previous character.
Incrementing Z wraps around to A. There is no previous character, so add the first one from this range, which is another A.
The range operator (..), when given two strings (and the left-hand one matches the pattern), uses the string increment to produce a list (this is explained near the end of that section). The list starts with the left-hand operand, which is then incremented until either:
The value equals the right-hand operand, or
The length of the value exceeds the length of the right-hand operand.
It returns a list of all the values. (If case 2 terminated the list, the final value is not included in it.)
Because (ignoring case for the moment; case is merely preserved, nothing interesting happens with it), 'AA' is the successor to 'Z', so how could it also be the successor to 'ZZ'? The successor to 'ZZ' is 'AAA'.
Because as far as ++ and all other numeric operators are concerned, "9z" is just a silly way of writing 9, and the successor to 9 is 10. The special string behavior of auto-increment is clearly specified to only occur on strings of letters, or strings of letters followed by numbers (and not mixed in any other way).
The answer is to not do that. The automagic incrementing of ++ with non-numbers is full of nasty pitfalls. It is suitable only for quick hacks.
You are better off writing your own iterator for this sort of thing:
#!/usr/bin/perl
use strict;
use warnings;
{ package StringIter;
sub new {
my $class = shift;
my %self = #_;
$self{set} = ["a" .. "z"] unless exists $self{set};
$self{value} = -1 unless exists $self{value};
$self{size} = #{$self{set}};
return bless \%self, $class;
}
sub increment {
my $self = shift;
$self->{value}++;
}
sub current {
my $self = shift;
my $n = $self->{value};
my $size = $self->{size};
my $s = "";
while ($n >= $size) {
my $offset = $n % $size;
$s = $self->{set}[$offset] . $s;
$n /= $size;
}
$s = $self->{set}[$n] . $s;
return $s;
}
sub next {
my $self = shift;
$self->increment;
return $self->current;
}
}
{
my $iter = StringIter->new;
for (1 .. 100) {
print $iter->next, "\n";
}
}
{
my $iter = StringIter->new(set => [0, 1]);
for (1 .. 7) {
print $iter->next, "\n";
}
}
You're asking why increment doesn't wrap around.
If it did it wouldn't really be an increment. To increment means you have a totally ordered set and an element in it and produce the next higher element, so it can never take you back to a lower element. In this case the total ordering is the standard alphabetical ordering of strings (which is only defined on the English alphabet), extended to cope with arbitrary ASCII strings in a way that seems natural for certain common types of identifier strings.
Wrapping would also defeat its purpose: usually you want to use it to generate arbitrarily many different identifiers of some sort.
I agree with Chas Owens's verdict: applying this operation to arbitrary strings is a bad idea, that's not the sort of use it was intended for.
I disagree with his remedy: just pick a simple starting value on which increment behaves sanely, and you'll be fine.
I don't see why incrementing Zz would return Aa; why do you think it should? 9z incrementing looks like Perl thinks 9z is a number 9 rather than some kind of base-36 weirdness.
=> In case of alpha-numeric strings starting with a character like 'bz' or 'Zz' start moving from the right.The first character is 'z'.As you say there is nowhere for 'z' to increment so it increments to 'a' but an extra carry is given over to the next digit on the left.So 'b' increments to 'c'. Now in the second case Z does not see any alphabet to the left of it.In such cases an extra copy of the current digit is created as it gets incremented.
=> In case of alpha-numeric strings starting with a digit like '9z', perl considers it as a mistake made by the user and considers it as the number which precedes the string (in this case 9) and increments the number. So 9 becomes 10.
Plz. correct me if I am wrong