Can you explain this nested conditional expression? - perl

I can't decipher this line of code. Could someone please translate it into if / else statements?
I understand the basic CONDITION ? VALUE_IF_TRUE : VALUE_IF_FALSE pattern, but this line seems to break that.
$type = $self->{1}{_flag} & 2 ?
$self->{2}{_flag} & 2 ? "A" : "B" :
$self->{2}{_flag} & 2 ? "B" : "C";

I think it's irresponsible to write code like that where the operator precedence is far from obvious
Using a mixture of if / else and conditional expressions it looks much clearer like this
if ( $self->{1}{_flag} & 2 ) {
$type = $self->{2}{_flag} & 2 ? "A" : "B";
}
else {
$type = $self->{2}{_flag} & 2 ? "B" : "C";
}

A couple of line breaks makes all the difference, although I've also added some parens to make it crystal clear.
$type = $self->{1}{_flag} & 2
? ( $self->{2}{_flag} & 2 ? "A" : "B" )
: ( $self->{2}{_flag} & 2 ? "B" : "C" );

That's almost as cryptic as the equivalent
$type = (qw/C x B x A/)[ $self->{1}{_flag} & 2 + $self->{2}{_flag} & 2 ];
More seriously, nesting ternary operators in the true-branch is confusing. I'd use an explicit if/else. Or if I really wanted a ternary, I'd rearrange this to:
$type = $self->{1}{_flag} & 2 != $self->{2}{_flag} & 2
? 'B'
: $self->{1}{_flag} & 2
? 'A'
: 'C';

Related

Perl simple way to do multiple decision such as grade scores (except if-elif)?

Try to seek Perl experts feedback on approaching this simple scores grading. What I came up is to use conditional opeartor to mimic multiway branch, but wonder if there's more simple/straight syntax to express to enhance the readability (and future maintainability).
Coming from Python/C, and pick up this new language, so I try to explore new syntax. if this sounds too naïve.
my $grade =
($score < 60) ? "F" :
($score < 68) ? "C" :
($score < 75) ? "B" :
($score < 90) ? "B+" :
($score < 95) ? "A" :
($score <= 100) ? "A+" :
"No Grade"; # catch-all default
You'd either need an hash array of 101 entries, or one with 21 and special cases. The optimization of using a hash array in this fashion would be premature without cause.
One would often use a hash or array as a dispatch table in similar situations, but it doesn't work here since you'd have to include all possible values.
Well, maybe not all possible value. For example, we could truncate the numbers to remove decimal points. And if all the value were divisible by 5, we divide by 5 to obtain a manageable 21 values. Then a dispatch table could be used. (Though an array would work better than a hash.)
That 68 means we can't do that. Either 65, 66 and 67, or 68 and 69 would need to be special-cased. The 100 is also a special value since you apparently want to tolerate invalid inputs. And that suggests a bad design unless performance is crucial.
Let's say the optimization was warranted.
You could setup the following tables:
my #grades_lkup;
$grades_lkup[ $_ ] = "F" for 0 .. 59;
$grades_lkup[ $_ ] = "C" for 60 .. 67;
$grades_lkup[ $_ ] = "B" for 68 .. 74;
$grades_lkup[ $_ ] = "B+" for 75 .. 89;
$grades_lkup[ $_ ] = "A" for 90 .. 94;
$grades_lkup[ $_ ] = "A+" for 95 .. 100;
Then all you'd need is
my $grade = $grades_lkup[ $score ] // "No Grade";

parsing text with Parse::RecDescent

I am trying to parse some text with Parse::RecDescent
from :
x=2 and y=2 and f=3 and (x has 3,4 or r=5 or r=6 ) and z=2
to something like :
x equal 2 and y equal 2 and f equal 3 and (( x contains 3 or x contains 4 or r equal 5 or requal 6 )) and z equal 2
other example :
input :
x= 3 and y has 5 and (z has 6 or z=3 ) and f=2
output :
x equals 3 and (( y contains 5)) and ((z has 6 or z equals 3)) and f equals 2
my question if i find a list of those operators :
has ,or
i should put "((" before the code and "))" after the code as mentioned on the example aboves
is it possible to do something like this with Parse::RecDescent ?
The grammar would look something like the following:
parse : expr EOF
expr : logic_or
# vvv Lowest precedence vvv
logic_or : <leftop: logic_and LOGIC_OR logic_and>
logic_and : <leftop: comparison LOGIC_AND comparison>
comparison : term comparison_[ $item[1] ]
comparison_ : '=' term
| HAS ident_list
|
# ^^^ Highest precedence ^^^
ident_list : <leftop: IDENT ',' IDENT>
term : '(' expr ')'
| IDENT
# Tokens
IDENT : /\w+/
LOGIC_OR : /or\b/
LOGIC_AND : /and\b/
HAS : /has\b/
EOF : /\Z/
Now you just need to add the code block to emit the desired output.
In comparison_, the LHS term is available as $arg[0].
I had to make some assumptions, so there could be errors.

Is there any function faster than find()?

I have two cell arrays "data1" "data2". For each line of "data1", I would find the number of lines that respects the condition on strings and numbers (see below) between "data1" and "data2". I tried to use the function "find" but I find that it is not very fast. Is there a function that is faster than "find" ? Here is my code:
for lineData1 = 1 : size(data1,1)
vectorData = find( ( cell2mat(data2(2:size(data2,1),1)) == data1{lineData1,2} | cell2mat(data2(2:size(data2,1),2)) == data1{lineData1,2} ) & ...
strcmp(data2(2:size(data2,1),4),data1{lineData1,3}) & ...
strcmp(data2(2:size(data2,1),5),data1{lineData1,4}) & ...
strcmp(data2(2:size(data2,1),6),data1{lineData1,5}) & ...
cell2mat(data2(2:size(data2,1),7)) == data1{lineData1,6} & ...
cell2mat(data2(2:size(data2,1),8)) == data1{lineData1,7} );
%%% vectorData = vectorData + 1;
data1{lineData1,1} = size(vectorData,1);
end
Thanks very much for your help.

Why differs German and Swiss Number to String Conversion?

I'm just trying to convert a float I read from Exel to a string with using a comma as decimal separator and two digits behind the comma.
I try the following code
$a = 707.63790474
$l = New-Object System.Globalization.CultureInfo("de-CH")
"CH: " + $a.ToString("F2", $l)
$l = New-Object System.Globalization.CultureInfo("de-DE")
"DE: " + $a.ToString("F2", $l)
$l = New-Object System.Globalization.CultureInfo("en-US")
"US: " + $a.ToString("F2", $l)
and get
CH: 707.64
DE: 707,64
US: 707.64
But to my knowledge a comma is used as decimal separator in Switzerland, unless it is a currency cf. http://en.wikipedia.org/wiki/Decimal_mark. Do I miss something?
Type (New-Object System.Globalization.CultureInfo("de-CH")).numberFormat to get numberformatinfo for de-CH
You'll get:
CurrencyDecimalDigits : 2
CurrencyDecimalSeparator : .
IsReadOnly : False
CurrencyGroupSizes : {3}
NumberGroupSizes : {3}
PercentGroupSizes : {3}
CurrencyGroupSeparator : '
CurrencySymbol : Fr.
NaNSymbol : n. def.
CurrencyNegativePattern : 2
NumberNegativePattern : 1
PercentPositivePattern : 1
PercentNegativePattern : 1
NegativeInfinitySymbol : -unendlich
NegativeSign : -
NumberDecimalDigits : 2
NumberDecimalSeparator : .
NumberGroupSeparator : '
CurrencyPositivePattern : 2
PositiveInfinitySymbol : +unendlich
PositiveSign : +
PercentDecimalDigits : 2
PercentDecimalSeparator : .
PercentGroupSeparator : '
PercentSymbol : %
PerMilleSymbol : ‰
NativeDigits : {0, 1, 2, 3...}
DigitSubstitution : None
As you can see both NumberDecimalSeparator and CurrencyDecimalSeparator are .
No, it looks like Switzerland uses . as the decimal separator (like normal people do), so this is correct output:
http://publib.boulder.ibm.com/infocenter/forms/v3r5m1/index.jsp?topic=%2Fcom.ibm.form.designer.locales.doc%2Fi_xfdl_r_formats_de_CH.html

What does !! (double exclamation point) mean?

In the code below, from a blog post by Alias, I noticed the use of the double exclamation mark !!. I was wondering what it meant and where I could go in the future to find explanations for Perl syntax like this. (Yes, I already searched for !! at perlsyn).
package Foo;
use vars qw{$DEBUG};
BEGIN {
$DEBUG = 0 unless defined $DEBUG;
}
use constant DEBUG => !! $DEBUG;
sub foo {
debug('In sub foo') if DEBUG;
...
}
UPDATE
Thanks for all of your answers.
Here is something else I just found that is related The List Squash Operator x!!
It is just two ! boolean not operators sitting next to each other.
The reason to use this idiom is to make sure that you receive a 1 or a 0. Actually it returns an empty string which numifys to 0. It's usually only used in numeric, or boolean context though.
You will often see this in Code Golf competitions, because it is shorter than using the ternary ? : operator with 1 and 0 ($test ? 1 : 0).
!! undef == 0
!! 0 == 0
!! 1 == 1
!! $obj   == 1
!! 100 == 1
undef ? 1 : 0 == 0
0 ? 1 : 0 == 0
1 ? 1 : 0 == 1
$obj ? 1 : 0 == 1
100 ? 1 : 0 == 1
not-not.
It converts the value to a boolean (or as close as Perl gets to such a thing).
Because three other answers claim that the range is "0" or "1", I just thought I'd mention that booleans in Perl (as returned by operators like ==, not, and so on) are undef and 1, not 0 and 1.