I am experiencing strange results with Perl's short circuited and, that is &&.
I am trying to solve a Project Euler problem where I want a certain number to be divisible by a list of numbers.
$b=42;
if($b%21==0 && b%2==0 && b%5==2){print "Why not?"};
Should print "Why not" as far as I can see, but keeps silent.
$b=42;
if($b%21==0 && b%2==0 && b%5==0){print "WTF?"};
Should keep silent, but prints "WTF?".
What gives?
As Rohit answered, the solution is to add the $ before the b. The exact reason it doesn't print "Why not?" but prints "WTF" is this: when you give the b without the $ sign (and without use strict; in force), Perl treats the b as the string "b". Then when you apply the operator % on it, since % is a numerical operator, Perl looks within the string "b" and checks whether it starts with a number. Since it doesn't, Perl takes the numerical value of "b" as 0, and then applies the mod (%) operation. 0%5 is 0 and not 2, so WTF is printed and not "Why not?"
Always use use strict and use warnings.
You are using your last two b's as bareword, which will be shown as warning - "Unquoted string "b" may clash with future reserved word"
You need to change your if to: -
if($b%21==0 && $b%2==0 && $b%5==2){print "Why not?"};
and: -
if($b%21==0 && $b%2==0 && $b%5==0){print "WTF?"};
gives expected results.
if($b%21==0 && $b%2==0 && $b%5==2){print "Why not?"};
works over here, you forgot the $, but apearntly you already found it :)
Related
First, please note that I ask this question out of curiosity, and I'm aware that using variable names like ## is probably not a good idea.
When using doubles quotes (or qq operator), scalars and arrays are interpolated :
$v = 5;
say "$v"; # prints: 5
$# = 6;
say "$#"; # prints: 6
#a = (1,2);
say "#a"; # prints: 1 2
Yet, with array names of the form #+special char like ##, #!, #,, #%, #; etc, the array isn't interpolated :
#; = (1,2);
say "#;"; # prints nothing
say #; ; # prints: 1 2
So here is my question : does anyone knows why such arrays aren't interpolated? Is it documented anywhere?
I couldn't find any information or documentation about that. There are too many articles/posts on google (or SO) about the basics of interpolation, so maybe the answer was just hidden in one of them, or at the 10th page of results..
If you wonder why I could need variable names like those :
The -n (and -p for that matter) flag adds a semicolon ; at the end of the code (I'm not sure it works on every version of perl though). So I can make this program perl -nE 'push#a,1;say"#a"}{say#a' shorter by doing instead perl -nE 'push#;,1;say"#;"}{say#', because that last ; convert say# to say#;. Well, actually I can't do that because #; isn't interpolated in double quotes. It won't be useful every day of course, but in some golfing challenges, why not!
It can be useful to obfuscate some code. (whether obfuscation is useful or not is another debate!)
Unfortunately I can't tell you why, but this restriction comes from code in toke.c that goes back to perl 5.000 (1994!). My best guess is that it's because Perl doesn't use any built-in array punctuation variables (except for #- and #+, added in 5.6 (2000)).
The code in S_scan_const only interprets # as the start of an array if the following character is
a word character (e.g. #x, #_, #1), or
a : (e.g. #::foo), or
a ' (e.g. #'foo (this is the old syntax for ::)), or
a { (e.g. #{foo}), or
a $ (e.g. #$foo), or
a + or - (the arrays #+ and #-), but not in regexes.
As you can see, the only punctuation arrays that are supported are #- and #+, and even then not inside a regex. Initially no punctuation arrays were supported; #- and #+ were special-cased in 2000. (The exception in regex patterns was added to make /[\c#-\c_]/ work; it used to interpolate #- first.)
There is a workaround: Because #{ is treated as the start of an array variable, the syntax "#{;}" works (but that doesn't help your golf code because it makes the code longer).
Perl's documentation says that the result is "not strictly predictable".
The following, from perldoc perlop (Perl 5.22.1), refers to interpolation of scalars. I presume it applies equally to arrays.
Note also that the interpolation code needs to make a decision on
where the interpolated scalar ends. For instance, whether
"a $x -> {c}" really means:
"a " . $x . " -> {c}";
or:
"a " . $x -> {c};
Most of the time, the longest possible text that does not include
spaces between components and which contains matching braces or
brackets. because the outcome may be determined by voting based on
heuristic estimators, the result is not strictly predictable.
Fortunately, it's usually correct for ambiguous cases.
Some things are just because "Larry coded it that way". Or as I used to say in class, "It works the way you think, provided you think like Larry thinks", sometimes adding "and it's my job to teach you how Larry thinks."
I see this awk idiom all the time and I would like to know exactly what it means
while (getline <"FILE" > 0)
I understand what getline is doing, but I don't get redirection to 0.
According to The Awk Programming Language, you do this to avoid a infinite loop in the case of FILE being nonexistant.
Why does redirection get rid of that error? What exactly is being redirected? It can't be the return value of getline, otherwise, what expression is while evaluating? I'm missing something!
EDIT: Thanks for the comments. I got redirection operators confused with relational operators. Perhaps it would be clearer if it was written like this
while ((getline <"FILE") > 0)
Or even clearer
while (0 < (getline <"FILE"))
The > sign is testing the return value from getline (for greater than zero). It is not a redirection sign.
From https://www.gnu.org/software/gawk/manual/html_node/Getline.html :
The getline command returns one if it finds a record and zero if it
encounters the end of the file. If there is some error in getting a
record, such as a file that cannot be opened, then getline returns -1.
In this case, gawk sets the variable ERRNO to a string describing the
error that occurred.
The confusion may come from the fact that output redirection to a file is not used by getline. (It is used only to read data). So there should be no ambiguity regarding the > sign. (It cannot mean output redirection)
To print something to a file you can use output redirection from the print or printf commands.
perldoc says "a list assignment in scalar context returns the number of elements on the right-hand side of the list assignment" but when I try this code:
perl -e '$_="aaaaa";print $v=(()=split //)'
The output is 1 which makes me confused. (The answer I expect is 5.)
Can anybody explain this?
According to split documentation:
When assigning to a list, if LIMIT is omitted, or zero, Perl supplies
a LIMIT one larger than the number of variables in the list <...>
Since you specify empty list, split only returns 1 result and this number of results is exactly what ends in your variable.
split has some kind of crazy ultra-magic in it that allows it to know when it is on the right hand side of an assignment that has a list on the left hand side, and adjusts its behavior according to the number of items in that list.
This is described in perlfunc as being done "to avoid unnecessary work", but you've found an observable difference in behavior caused by that optimization.
To see some evidence of what happened, run your script through Deparse like this:
perl -MO=Deparse -e '$_="aaaaa";print $v=(()=split //)'
Update: I went looking for the code that implements this, and it's not where I expected it to be. Actually the optimization is performed by the assignment operator (op.c:Perl_newASSIGNOP) . split doesn't know that much about its context.
Why are you assigning to an empty array? the ()=(split //) bit. That's going to end up with - um, well, a mess. Or, in your case, an array with a size of one with not much in it.
Also, that's excessively obscure. perl has a sad reputation for being write-only, and all that modifying $_ and using it doesn't help others - or you - understand what is going on.
Try something like
perl -e '$v = (split //, "aaaaa"); print "$v\n"'
or, if you wish to replicate the behavior of your test:
perl -e '$v = () = (split //, "aaaaa"); print "$v\n"'
Yes, but :
perl -e '$_="aaaaa";print $v=(split //)'
gives 5, as well as
perl -e '$_="aaaaa";print $v=(#x=split //)'
Maybe your left-value () is dropping additional array elements ?
edit : by the way :
perl -e '$_="aaaaa";print $v=(($x,$y)=split //)'
returns 3, because the right sight of the $v=... command gives :
( $x , $y , ( a , a , a ) )
So in your original case, ()=split // returns ( ( a , a , a , a , a ) ) (which has only one element)
Edit : bad array notation, and result was wrong because of a last minute changed of my test-case
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How do I fix this Perl code so that 1.1 + 2.2 == 3.3?
I'm working on a Perl script that compares strings representing gene models and prints out a summary of the comparison. If the gene models match perfectly, I print out a very terse summary, but if they are different, the summary is quite verbose.
The script looks at the value of a variable to determine whether it should do the terse or verbose summary--if the variable is equal to 1, it should print the terse summary; otherwise, it should print the verbose summary.
Since the value is numeric (a float), I've been using the == operator to do the comparison.
if($stats->{overall_simple_matching_coefficient} == 1)
{
print "Gene structures match perfectly!\n";
}
This worked correctly for all of my tests and even for most of the new cases I am running now, but I found a weird case where the value was equal to 1 but the above comparison failed. I have not been able to figure out why the comparison failed, and stranger yet, when I changed the == operator to the eq operator, it seemed to work fine.
I thought the == was for numerical comparison and eq was for string comparison. Am I missing something here?
Update: If I print out the value right before the comparison...
printf("Test: '%f', '%d', '%s'\n", $stats->{overall_simple_matching_coefficient}, $stats->{overall_simple_matching_coefficient}, $stats->{overall_simple_matching_coefficient});
...I get this.
Test: '1.000000', '0', '1'
The first thing any computer language teacher should teach you about any computer language is that YOU CANNOT COMPARE FLOATS FOR EQUALITY. This is true of any language. Floating point arithmetic is not exact, and two floats that look like they're the same will be different in the insignificant digits somewhere where you can't see it. Instead, you can only compare that they are close to each other - like
if (abs(stats->{overall_simple_matching_coefficient)-1) < 0.0001)
What do you get if you print the value of $stats->{overall_simple_matching_coefficient} just before the comparison? If it's 1, try printf with a format of "%20.10f". I strongly suspect you have some rounding error (less then 1e-6) accumulated in the variable and it's not comparing equal numerically. However when converted to string, since the error is right of the 6th decimal place, and the default string format is to six places, it compares equal.
What is the best way to log an error when testing in Perl? I prefer a one-liner solution such as
return 0 if $var!=3 and $logger->error("Var is not 3"))
Can something like the example shown be implemented?
You could always do:
$logger->error("Var is not 3") and return 0 if $var != 3
which takes advantage of and's low precedence (and if's even lower precedence).
$logger->error("Var is not 3"), return 0 if $var != 3;
This is taking advantage of Perl's support for the comma operator, which allows you to write an expression where parts to the left are evaluated but their value ignored, with the final value of the expression being the rightmost part.
I prefer using carp/croak/confess if it's just me debugging, but if you're doing it for production code, maybe try Log::Report.
So something like: $var == 3 or confess("var != 3!") is what I'd use if I wanted a one liner that prints an error and a stacktrace.
Log4Perl may do what you'd like. This is the Perl clone of Log4J. It has fine grain dynamic log filtering configuration.
If you're using Test::More, use diag(). This plays better with prove and the like.