Use of logical operators AND OR when comparing conditionals - perl

I am facing some doubts about the use (and return values I guess) of the logical operators &&, and, ||, or.
$number = 5;
$numberA = 5;
$numberB = 1;
$string = "x";
$stringA = "x";
$stringB = "y";
If two numbers are compared:
$x=5;
if ( $x == $number ) { print '$x == $number', "\n"; }
If two strings are compared:
$x="x";
if ( $x eq $string ) { print '$x eq $string', "\n"; }
But I'm not sure what is the best way to evaluate two numbers/strings to a number/string. Is this correct?
$x=5; $y=5;
if ( ($x && $y) == $number ) { print '($x && $y) == $number', "\n"; }
$x="x"; $y="x";
if ( ($x and $y) eq $string ) { print '($x and $y) eq $string', "\n"; }
And what is the rule when two logicals are evaluated in the same condition? Should the conditions itself be compared as numbers (&&,||) or strings (and,or)?
$x=5; $y=1;
if ( ($x == $numberA) && ($y == $numberB) ) { print '&& or and here?', "\n"; }
$x="x"; $y="y";
if ( ($x eq $stringA) and ($y eq $stringB) ) { print 'and or or here?', "\n"; }

( $foo && $bar ) == $baz
does not do what you think it does; it first evaluates the && operation, getting the value of $foo if $foo is true and otherwise getting the value of $bar, then compares that to $baz. You need to explicitly spell it out as $foo == $baz && $bar == $baz to test both.
If you have many values (preferably in an array, not a bunch of separate variables), grep can be useful:
if ( 2 == grep $_ == $baz, $foo, $bar ) {
List::MoreUtils provides a convenient all method, too:
use List::MoreUtils 'all';
if ( all { $_ == $baz } $foo, $bar ) {
and/or and &&/|| are not string or numeric operators; the alphabetic ones function exactly the same as the equivalent symbolic ones. The only difference is that they have different precedence; &&/|| have a higher precedence, such that they are useful within an expression; and/or have a lower precedence, such that they are useful for flow control between what are essentially different expressions. Some examples:
my $x = $y || 'default_value';
equivalent to:
my $x = ( $y || 'default_value' );
vs.
my #a = get_lines() or die "expected some lines!";
equivalent to:
( my #a = get_lines() ) or die "expected some lines!";

Related

Perl logical and with multiple tests

Is it normal to write in such way:
if(($x && $y && $z) eq "test"){
}
If I am interested in ($x eq "test") && ($y eq "test") && ($z eq "test")?
It wouldn't be normal because it doesn't even work
$x && $y returns either $x or $y depending on whether the value of $x is true or not.
That means that ($x && $y) && $z returns either $x, $y or $z depending on the values of $x and $y.
That means that only one of $x, $y or $z will be compared against 'test'.
For example, you'll get a false positive (returns true when it should return false) when you have $x='abc'; $y='def'; $z='test';.
If you wanted something shorter, then you'd have to use something like
use List::MoreUtils qw( all );
if ( all { $_ eq "test" } $x, $y, $z )
No.
The expression $x && $y && $z is equivalent to the expression
$x ? $x : $y ? $y : $z
and will be evaluated separately from the expression on the other side of the eq operator.
Writing if ($x eq 'test' && $y eq 'test' && $z eq 'test') ... as you do is as reasonably terse as you're going to get.
As an elaboration on ikegami's answer, I thought I would show the code for the all() subroutine, as it is rather simple, and gives a good demonstration of the logic behind the answer to the question. And also, as it happens, makes a good demonstration for how prototypes work.
sub all (&#) {
my $f = shift;
foreach ( #_ ) {
return NO unless $f->();
}
return YES;
}
The prototypes & and # refer to a code reference, and a list of arguments. YES and NO are constants for true and false defined as follows:
# Use pure scalar boolean return values for compatibility with XS
use constant YES => ! 0;
use constant NO => ! 1;
The basic gist of this subroutine is to take a code reference (an anonymous subroutine) and execute it once for each argument, using $_ as the loop variable. If any argument causes the code block to return a false value, the subroutine returns false and ends.
With this code and these prototypes we can use the all subroutine much like map, with a code block first:
all { $_ eq "foo" } "foo", "bar", "baz";
If we remove the prototypes (&#), we must pass the code reference as a standalone, as part of the list:
all sub { $_ eq "foo" }, "foo", "bar", "baz";
Note the addition of the sub keyword, and the comma after the code block.

perl defensive programming (die, assert, croak)

What is the best (or recommended) approach to do defensive programming in perl?
For example if I have a sub which must be called with a (defined) SCALAR, an ARRAYREF and an optional HASHREF.
Three of the approaches I have seen:
sub test1 {
die if !(#_ == 2 || #_ == 3);
my ($scalar, $arrayref, $hashref) = #_;
die if !defined($scalar) || ref($scalar);
die if ref($arrayref) ne 'ARRAY';
die if defined($hashref) && ref($hashref) ne 'HASH';
#do s.th with scalar, arrayref and hashref
}
sub test2 {
Carp::assert(#_ == 2 || #_ == 3) if DEBUG;
my ($scalar, $arrayref, $hashref) = #_;
if(DEBUG) {
Carp::assert defined($scalar) && !ref($scalar);
Carp::assert ref($arrayref) eq 'ARRAY';
Carp::assert !defined($hashref) || ref($hashref) eq 'HASH';
}
#do s.th with scalar, arrayref and hashref
}
sub test3 {
my ($scalar, $arrayref, $hashref) = #_;
(#_ == 2 || #_ == 3 && defined($scalar) && !ref($scalar) && ref($arrayref) eq 'ARRAY' && (!defined($hashref) || ref($hashref) eq 'HASH'))
or Carp::croak 'usage: test3(SCALAR, ARRAYREF, [HASHREF])';
#do s.th with scalar, arrayref and hashref
}
use Params::Validate qw(:all);
sub Yada {
my (...)=validate_pos(#_,{ type=>SCALAR },{ type=>ARRAYREF },{ type=>HASHREF,optional=>1 });
...
}
I wouldn't use any of them. Aside from not not accepting many array and hash references, the checks you used are almost always redundant.
>perl -we"use strict; sub { my ($x) = #_; my $y = $x->[0] }->( 'abc' )"
Can't use string ("abc") as an ARRAY ref nda"strict refs" in use at -e line 1.
>perl -we"use strict; sub { my ($x) = #_; my $y = $x->[0] }->( {} )"
Not an ARRAY reference at -e line 1.
The only advantage to checking is that you can use croak to show the caller in the error message.
Proper way to check if you have an reference to an array:
defined($x) && eval { #$x; 1 }
Proper way to check if you have an reference to a hash:
defined($x) && eval { %$x; 1 }
None of the options you show display any message to give a reason for the failure, which I think is paramount.
It is also preferable to use croak instead of die from within library subroutines, so that the error is reported from the point of view of the caller.
I would replace all occurrences of if ! with unless. The former is a C programmer's habit.
I suggest something like this
sub test1 {
croak "Incorrect number of parameters" unless #_ == 2 or #_ == 3;
my ($scalar, $arrayref, $hashref) = #_;
croak "Invalid first parameter" unless $scalar and not ref $scalar;
croak "Invalid second parameter" unless $arrayref eq 'ARRAY';
croak "Invalid third parameter" if defined $hashref and ref $hashref ne 'HASH';
# do s.th with scalar, arrayref and hashref
}

Perl Mismatch among arrays

I have two arrays:
#array1 = (A,B,C,D,E,F);
#array2 = (A,C,H,D,E,G);
The arrays could be of different size. I want to find how many mismatches are there between the arrays. The indexes should be the same. In this case there are three mismatch :b->c,c->h and F->G.(i.e , The 'C' in $array[2] should not be considered a match to 'C' in $array[1]) I would like to get the number of mismatches as well as the mismatch.
foreach my $a1 ( 0 .. $#array1) {
foreach my $a2( 0 .. $#array2)
if($array1[$a1] ne $array2[$a2]) {
}
}
}
my %array_one = map {$_, 1} #array1;
my #difference = grep {!$array_one {$_}} #array1;
print "#difference\n";
Ans: gives me H, G but not C.
with my little Perl knowledge I tried this, with no result. Could you suggest me how I should deal this? Your suggestions and pointers would be very helpful.
You shouldn't have nested loops. You only need to go through the indexes once.
use List::Util qw( max );
my #mismatches;
for my $i (0..max($#array1, $#array2)) {
push #mismatches, $i
if $i >= #array1
|| $i >= #array2
|| $array1[$i] ne $array2[$i];
}
}
say "There are " . (0+#mismatches) . " mismatches";
for my $i (#mismatches) {
...
}
Since you mentioned grep, this is how you'd replace the for with grep:
use List::Util qw( max );
my #mismatches =
grep { $_ >= #array1
|| $_ >= #array2
|| array1[$_] ne $array2[$_] }
0 .. max($#array1, $#array2);
say "There are " . (0+#mismatches) . " mismatches";
for my $i (#mismatches) {
...
}
Here's an example using each_arrayref from List::MoreUtils.
sub diff_array{
use List::MoreUtils qw'each_arrayref';
return unless #_ && defined wantarray;
my #out;
my $iter = each_arrayref(#_);
my $index = 0;
while( my #current = $iter->() ){
next if all_same(#current);
unshift #current, $index;
push #out, \#current;
}continue{ ++$index }
return #out;
}
This version should be faster if you are going to use this for determining the number of differences often. The output is exactly the same. It just doesn't have to work as hard when returning a number.
Read about wantarray for more information.
sub diff_array{
use List::MoreUtils qw'each_arrayref';
return unless #_ && defined wantarray;
my $iter = each_arrayref(#_);
if( wantarray ){
# return structure
my #out;
my $index = 0;
while( my #current = $iter->() ){
next if all_same(#current);
unshift #current, $index;
push #out, \#current;
}continue{ ++$index }
return #out;
}else{
# only return a count of differences
my $out = 0;
while( my #current = $iter->() ){
++$out unless all_same #current;
}
return $out;
}
}
diff_array uses the subroutine all_same to determine if all of the current list of elements are the same.
sub all_same{
my $head = shift;
return undef unless #_; # not enough arguments
for( #_ ){
return 0 if $_ ne $head; # at least one mismatch
}
return 1; # all are the same
}
To get just the number of differences:
print scalar diff_array \#array1, \#array2;
my $count = diff_array \#array1, \#array2;
To get a list of differences:
my #list = diff_array \#array1, \#array2;
To get both:
my $count = my #list = diff_array \#array1, \#array2;
The output for the input you provided:
(
[ 1, 'B', 'C' ],
[ 2, 'C', 'H' ],
[ 5, 'F', 'G' ]
)
Example usage
my #a1 = qw'A B C D E F';
my #a2 = qw'A C H D E G';
my $count = my #list = diff_array \#a1, \#a2;
print "There were $count differences\n\n";
for my $group (#list){
my $index = shift #$group;
print " At index $index\n";
print " $_\n" for #$group;
print "\n";
}
You're iterating over both arrays when you don't want to be doing so.
#array1 = ("A","B","C","D","E","F");
#array2 = ("A","C","H","D","E","G");
foreach my $index (0 .. $#array1) {
if ($array1[$index] ne $array2[$index]) {
print "Arrays differ at index $index: $array1[$index] and $array2[$index]\n";
}
}
Output:
Arrays differ at index 1: B and C
Arrays differ at index 2: C and H
Arrays differ at index 5: F and G
Well, first, you're going to want to go over each element of one of the arrays, and compare it to the same element of the other array. List::MoreUtils provides an easy way to do this:
use v5.14;
use List::MoreUtils qw(each_array);
my #a = qw(a b c d);
my #b = qw(1 2 3);
my $ea = each_array #a, #b;
while ( my ($a, $b) = $ea->() ) {
say "a = $a, b = $b, idx = ", $ea->('index');
}
You can extend that to find where there is a non-match by checking inside that while loop (note: this assumes your arrays don't have undefs at the end, or that if they do, undef is the same as having a shorter array):
my #mismatch;
my $ea = each_array #a, #b;
while ( my ($a, $b) = $ea->() ) {
if (defined $a != defined $b || $a ne $b) {
push #mismatch, $ea->('index');
}
}
and then:
say "Mismatched count = ", scalar(#mismatch), " items are: ", join(q{, }, #mismatch);
The following code builds a list of mismatched pairs, then prints them out.
#a1 = (A,B,C,D,E,F);
#a2 = (A,C,H,D,E,G);
#diff = map { [$a1[$_] => $a2[$_]] }
grep { $a1[$_] ne $a2[$_] }
(0..($#a1 < $#a2 ? $#a1 : $#a2));
print "$_->[0]->$_->[1]\n" for #diff
You have the right idea, but you only need a single loop, since you are looking at each index and comparing entries between the arrays:
foreach my $a1 ( 0 .. $#array1) {
if($array1[$a1] ne $array2[$a1]) {
print "$a1: $array1[$a1] <-> $array2[$a1]\n";
}
}

Differences beteween switch- and if-statements

Do these two statements behave equally or could they yield different results?
if ( ... ) {...}
elsif( ... ) {... }
elsif( ... ) { ... }
else { ... }
.
given ( ... ) {
when ( ... ) { ... }
when ( ... ) { ... }
default { ... }
}
I've found the problem - with a modified ninth "when" it works now.
...
no warnings qw(numeric);
my $c = &getch();
given ( $c ) {
when ( $c == $KEY_LEFT and 1 > 0 ) { say 1; say $c }
when ( $c == $KEY_RIGHT ) { say 2; say $c }
when ( $c eq "\cH" or $c eq "\c?" ) { say 3; say $c }
when ( $c eq "\cC" ) { say 4; say $c }
when ( $c eq "\cX" or $c eq "\cD" ) { say 5; say $c }
when ( $c eq "\cA" ) { say 6; say $c }
when ( $c eq "\cE" ) { say 7; say $c }
when ( $c eq "\cL" ) { say 8; say $c }
when ( not( not $SpecialKey{$c} ) ) { say 9; say $c }
when ( ord( $c ) >= 32 ) { say 10; say $c }
default { say 11; say $c }
}
if ( $c == $KEY_LEFT and 1 > 0 ) { say 1; say $c }
elsif ( $c == $KEY_RIGHT ) { say 2; say $c }
elsif ( $c eq "\cH" or $c eq "\c?" ) { say 3; say $c }
elsif ( $c eq "\cC" ) { say 4; say $c }
elsif ( $c eq "\cX" or $c eq "\cD" ) { say 5; say $c }
elsif ( $c eq "\cA" ) { say 6; say $c }
elsif ( $c eq "\cE" ) { say 7; say $c }
elsif ( $c eq "\cL" ) { say 8; say $c }
elsif ( $SpecialKey{$c} ) { say 9; say $c }
elsif ( ord( $c ) >= 32 ) { say 10; say $c }
else { say 11; say $c }
close TTYIN;
Your supposedly "fixed" version now does different things in the two versions of the code. Checking if a key exists in a hash is completely different to checking whether the associated value is true.
There are three different truth values you can get from a hash - whether the key exists in the hash, whether the associated value is defined and whether the associated value is true or false. This code should demonstrate the difference between the three:
#!/usr/bin/perl
use strict;
use warnings;
my %hash = (
key1 => undef,
key2 => 0,
key3 => 1,
);
foreach (qw(key1 key2 key3 key4)) {
check_key($_);
}
sub check_key {
my $k = shift;
print "Key $k ";
if (exists $hash{$k}) {
print 'exists. ';
} else {
print "doesn't exist. ";
}
print 'Value ';
if (defined $hash{$k}) {
print 'is defined ';
} else {
print 'is not defined ';
}
print 'and is ';
if ($hash{$k}) {
print "true\n";
} else {
print "false\n";
}
}
Whatever you can do with the given/when, you can do with if/elsif/else. The idea is that when/given is suppose to be easier to read, and can automatically use smartmatching by default while you have to specify smart matching with the if/else statement.
I didn't parse through your code to make sure they're exact equivalents, but it looks like you've got more or less the right idea about if/elsif/else and given/when.
I never really understood the desire for what was referred to as the switch statement. It was something Perl coders always complained about -- the lack of a switch statement in Perl. Maybe it's a C thing that most Perl developers fondly remember. But I never found the if/elsif/else statements that bad.
What really confuses me is that when they finally implemented the switch statement, they didn't call it switch. Why the heck not?
And why say instead of printnl?
And, why last and next instead of break and continue. Why not simply use the standard names that other languages already use?
But enough of this Seinfeld style whining...
As davorg said, there's a big difference between a hash key that doesn't exist, a hash key that exists, but isn't defined, and a hash key that's defined, but gets evaluated to false:
For example:
use strict;
use warnings;
no warnings qw(uninitialized);
my %hash;
$hash{FOO} = "bar";
if (not exists($hash{BAR})) {
print "\$hash{FOO} doesn't exist\n";
}
if (not defined($hash{BAR})) {
print "\$hash{BAR} is undefined\n";
}
if (not $hash{BAR}) {
print "\$hash{BAR} evaluates to false\n";
}
if ($hash{BAR} eq undef) {
print "\$hash{BAR} is equal to 'undef'\n";
}
You can see that $hash{BAR} doesn't even exist as a key in the %hash hash, but it is also undefined, and it evaluates as false. And, you can also see that it also evaluates as undef (notice I had to set no warnings qw(uninitialized); to prevent it from complaining about $hash{BAR} being uninitialized in my last if statement).
However, if I do this:
$hash{FOO} = bar;
$hash{BAR} = undef;
The first if statement no longer evaluates as true because the key BAR now does exist in the hash, but the value is still undefined and still evaluates as false.
And, if I did this:
$hash{FOO} = bar;
$hash{BAR} = 0;
$hash{BAR} now exists as a key in %hash, and it is no longer undefined, but it still evaluates as false.
I like simply being able to say this:
if (not $hash{BAR}) {
because it is short and sweet, and probably does what I want. However, I do have to understand the difference between existence of a key in a hash, evaluation to false, and not being defined as three separate things. It can be important if you have a subroutine that could return a NULL string or zero value:
if (not foo($bar)) {
die qq(Error of some sort\n);
}
sub foo {
$bar = <FOO> or return;
return chomp($bar);
}
If there's a blank line in my file, it'll return a NULL string, but the subroutine will still return a defined value. The above probably does not do what I want.
Adding another answer as I've just realised what the real problem is.
Your "when ($SpecialKey{$c})" is equivalent to "if ($_ ~~ $SpecialKey{$c})". And as "given" has set $_ to $c that's the same as "if ($c ~~ $SpecialKey{$c})". When comparing two scalar values (and I assume that's what we've got here) the smart match operator looks for values that are numbers and uses "eq" or "==" as appropriate.
So your code is effectively equivalent to "if ($c == $SpecialKey{$c})". And that's completely different to "if ($SpecialKey{$c})".
They behave totally equal.
given / when has implicit smart matching:
Most of the power comes from implicit smart matching:
when($foo)
is exactly equivalent to
when($_ ~~ $foo)
I don't think if/else does that(?)
See perlsyn
EDIT:
Guess this doesn't really matter when using given/when the way OP is, but it still answers the question :)

How can I check if all elements of an array are identical in Perl?

I have an array #test. What's the best way to check if each element of the array is the same string?
I know I can do it with a foreach loop but is there a better way to do this? I checked out the map function but I'm not sure if that's what I need.
If the string is known, you can use grep in scalar context:
if (#test == grep { $_ eq $string } #test) {
# all equal
}
Otherwise, use a hash:
my %string = map { $_, 1 } #test;
if (keys %string == 1) {
# all equal
}
or a shorter version:
if (keys %{{ map {$_, 1} #test }} == 1) {
# all equal
}
NOTE: The undefined value behaves like the empty string ("") when used as a string in Perl. Therefore, the checks will return true if the array contains only empty strings and undefs.
Here's a solution that takes this into account:
my $is_equal = 0;
my $string = $test[0]; # the first element
for my $i (0..$#test) {
last unless defined $string == defined $test[$i];
last if defined $test[$i] && $test[$i] ne $string;
$is_equal = 1 if $i == $#test;
}
Both methods in the accepted post give you the wrong answer if #test = (undef, ''). That is, they declare an undefined value to be equal to the empty string.
That might be acceptable. In addition, using grep goes through all elements of the array even if a mismatch is found early on and using the hash more than doubles the memory used by elements of array. Neither of these would be a problem if you have small arrays. And, grep is likely to be fast enough for reasonable list sizes.
However, here is an alternative that 1) returns false for (undef, '') and (undef, 0), 2) does not increase the memory footprint of your program and 3) short-circuits as soon as a mismatch is found:
#!/usr/bin/perl
use strict; use warnings;
# Returns true for an empty array as there exist
# no elements of an empty set that are different
# than each other (see
# http://en.wikipedia.org/wiki/Vacuous_truth)
sub all_the_same {
my ($ref) = #_;
return 1 unless #$ref;
my $cmpv = \ $ref->[-1];
for my $i (0 .. $#$ref - 1) {
my $this = \ $ref->[$i];
return unless defined $$cmpv == defined $$this;
return if defined $$this
and ( $$cmpv ne $$this );
}
return 1;
}
However, using List::MoreUtils::first_index is likely to be faster:
use List::MoreUtils qw( first_index );
sub all_the_same {
my ($ref) = #_;
my $first = \ $ref->[0];
return -1 == first_index {
(defined $$first != defined)
or (defined and $_ ne $$first)
} #$ref;
}
TIMTOWTDI, and I've been reading a lot of Mark Jason Dominus lately.
use strict;
use warnings;
sub all_the_same {
my $ref = shift;
return 1 unless #$ref;
my $cmp = $ref->[0];
my $equal = defined $cmp ?
sub { defined($_[0]) and $_[0] eq $cmp } :
sub { not defined $_[0] };
for my $v (#$ref){
return 0 unless $equal->($v);
}
return 1;
}
my #tests = (
[ qw(foo foo foo) ],
[ '', '', ''],
[ undef, undef, undef ],
[ qw(foo foo bar) ],
[ '', undef ],
[ undef, '' ]
);
for my $i (0 .. $#tests){
print "$i. ", all_the_same($tests[$i]) ? 'equal' : '', "\n";
}
You can check how many times the element in the array (#test) is repeated by counting it in a hash (%seen). You can check how many keys ($size) are present in the hash (%seen). If more than 1 key is present, you know that the elements in the array are not identical.
sub all_the_same {
my #test = #_;
my %seen;
foreach my $item (#test){
$seen{$item}++
}
my $size = keys %seen;
if ($size == 1){
return 1;
}
else{
return 0;
}
}
I think, we can use List::MoreUtils qw(uniq)
my #uniq_array = uniq #array;
my $array_length = #uniq_array;
$array_length == 1 ? return 1 : return 0;
I use List::Util::first for all similar purposes.
# try #0: $ok = !first { $_ ne $string } #test;
# try #1: $ok = !first { (defined $_ != defined $string) || !/\A\Q$string\E\z/ } #test;
# final solution
use List::Util 'first';
my $str = shift #test;
my $ok = !first { defined $$_ != defined $str || defined $str && $$_ ne $str } map \$_, #test;
I used map \$_, #test here to avoid problems with values that evaluate to false.
Note. As cjm noted fairly, using map defeats the advantage of first short-circuiting. So I tip my hat to Sinan with his first_index solution.