Reputation: 563
I'm trying to write more efficient code in my scripts and have been implementing ternary conditional operators on occasion. I can't understand why I am getting an additional result when using a ternary conditional operator in a loop:
#!/usr/bin/perl
use strict;
use warnings;
my @array = ('Serial = "123"', 'Serial = "456"', 'Serial = "789"');
my ($test1,$test2);
foreach my $a (@array){
!$test1 ? $test1 = $a : $test1 .= " AND " . $a;
}
foreach my $b (@array){
if (!$test2) {
$test2 = $b
} else {
$test2 .= " AND " . $b;
}
}
print "Test1: $test1\n";
print "Test2: $test2\n";
Output:
~/bin/test.pl
Test1: Serial = "123" AND Serial = "123" AND Serial = "456" AND Serial = "789"
Test2: Serial = "123" AND Serial = "456" AND Serial = "789"
Test1 output has an additional "Serial = "123", What am I doing wrong?
Upvotes: 4
Views: 4816
Reputation: 19020
Assignment has lower precendence than ?
. This
!$test1 ? $test1 = $a : $test1 .= " AND " . $a;
is equivalent to this:
(!$test1 ? $test1 = $a : $test1) .= " AND " . $a;
So first $test1
will become Serial = "123"
and then AND Serial = "123"
gets appended immediately after.
Try this:
!$test1 ? ($test1 = $a) : ($test1 .= " AND " . $a);
A better solution would be this:
$test1 = !$test1 ? $a : $test1 . " AND " . $a;
Using the ternary operator for side effects can get quite messy and I'd recommend to avoid it.
Edit
As noted by MuIsTooShort join(' AND ', array)
would be the most concise and readable solution in your case.
Upvotes: 7
Reputation: 1186
In your loop
my ($test1,$test2);
foreach my $a (@array){
!$test1 ? $test1 = $a : $test1 .= " AND " . $a;
}
1st time $test1
will be :Serial = "123" AND Serial = "123"
2nd time : Serial ="123" AND Serial = "123" AND Serial = "456"
3rd:Serial "123" AND Serial = "123" AND Serial = "456" AND Serial = "789"
4ht:Test1: Serial = "123" AND Serial = "123" AND Serial = "456" AND Serial = "789"
Upvotes: 0
Reputation: 385645
First, you have a precedence problem.
!$test1 ? $test1 = $a : $test1 .= " AND " . $a;
means
( !$test1 ? $test1 = $a : $test1 ) .= " AND " . $a;
It can be solved with parens.
my $test1;
for (@array) {
!$test1 ? ($test1 = $a) : ($test1 .= " AND " . $a);
}
But that's not readable. You're obviously going in the wrong direction! There are two tasks being performed, and you are trying to jam them into one. Simply separating them makes the code much more readable.
my $test1;
for (@array) {
$test1 .= ' AND ' if $test1;
$test1 .= $_;
}
But we're not there yet. Let me introduce you to join
.
my $test1 = join(' AND ', @array);
So much better!
Finally, it sure looks like you are building an SQL statement. If so, your question is moot since you should USE PLACEHOLDERS TO TRANSFER DATA TO A DATABASE. Search the DBI documentation for that word.
Upvotes: 11
Reputation: 37136
The ternary operator is great for situations when Z = X OR Y
.
You're not really doing that here. What's happening is that you're building a string from an array. That's what the join
function is all about.
my $test = join ' AND ', @array;
Now that is more efficient coding.
Upvotes: 2
Reputation: 5893
You're hitting a precedence problem. You can see how Perl has interpreted (i.e. understood) an expression using Deparse
perl -MO=Deparse,-p -e "!$test1 ? $test1 = $a : $test1 .= q{ AND } . $a;"
the -p
tells Deparse to put in lots of brackets so you can see what's up:
(((!$test1) ? ($test1 = $a) : $test1) .= (' AND ' . $a));
so here you can see that the line of code doesn't mean what you intended.
It's fairly bad style to use ?:
like this, and won't make things faster.
Upvotes: 2