Logical problem with "one" junction in Perl6 - range

I am having some trouble with .one junction and range match:
> say (3,5).any ~~ (1 .. 9)
any(True, True)
> say so (3,5).any ~~ (1 .. 9)
True
> say so (3,5).one ~~ (1 .. 9)
False
> say so (3,0).one ~~ (1 .. 9) # expect True because 0 not in range and 3 is
False
> say so (3,0).any ~~ (1 .. 9)
True
> say so (0, 3).one ~~ (1..9) # expected True; 0 not in range; exactly one item (3) is in range
False
> so 0 ~~ 1..9 # as expected;
False
> so 3 ~~ 1..9
True
> say (0, 3).one ~~ (1..9) # unexpected; 0 not in range;
one(True, True) # expected one(False, True)
> say (1..9).elems
9
> say (0, 10).one ~~ (1..9) # why is it true that 0 ~~ 1..9 ??
one(True, False)
> say so (0, 10).one ~~ (1..9) # unexpected !!! neither 0 nor 10 in range
True
> say (-1, 3).one ~~ (1..9) # why -1 in range of 1..9 ??
one(True, True)
>
What am I missing? I am using Rakudo Star 2018.10 on MoarVM, implementing Perl6.c.

I think the underlying problem is this:
$ perl6 -e 'dd (0,3).one ~~ (1 .. 9)'
one(Bool::True, Bool::True)
That should be one(Bool::False, Bool::True). I think this is a bug, worthy of making an issue for.

Related

Undef value assigned to variable.

The return value of 5 > 6 is undef. However when I assign the value of 5 > 6 to a variable that variable is defined. How do I pass the value of a statement using a comparison operator that resolves to undef on failure?
#!/usr/bin/perl
use strict ;
use warnings;
print'Five is more than six ? ', 5 > 6, "\n";
print 'Five is less than six ? ' , 5 < 6 , "\n";
my $wiz = 5 > 6 ;
if (defined($wiz)) {
print '$wiz is defined' ;
} else {
print '$wiz is undefined' ;
}
$ ./lessthan
Five is more than six ?
Five is less than six ? 1
$wiz is defined
How do I pass the value of a statement using a comparison operator that resolves to undef on failure?
Generally speaking, Perl doesn't promise to return any specific true or false value from its operators, and < is no exception. If you want specific values, you will need something like
$boolean ? $value_for_true : $value_for_false
so
my $wiz = 5 > 6 ? 1 : undef;
If you only care about the value you get for false, you could also use the following:
my $wiz = 5 > 6 || undef;
The two options turn out to be equivalent, but this isn't guaranteed.
The return value of 5 > 6 is undef. However when I assign the value of 5 > 6 to a variable
That's not true. The variable is assigned a defined value because 5 > 6 evaluated to a defined value. While Perl doesn't specify what false value it returns from it's operators, it usually returns the scalar sv_no, which is a dualvar that appears to be an empty string when treated as a string, and zero when treated as a number.
$ perl -wE'say "> ", "".( undef )'
Use of uninitialized value in concatenation (.) or string at -e line 1.
>
$ perl -wE'say "> ", "".( "" )'
>
$ perl -wE'say "> ", "".( 0 )'
> 0
$ perl -wE'say "> ", "".( 5>6 )'
> # Behaves as ""
$ perl -wE'say "> ", 0+( undef )'
Use of uninitialized value in addition (+) at -e line 1.
> 0
$ perl -wE'say "> ", 0+( "" )'
Argument "" isn't numeric in addition (+) at -e line 1.
> 0
$ perl -wE'say "> ", 0+( 0 )'
> 0
$ perl -wE'say "> ", 0+( 5>6 )'
> 0 # Behaves as 0
5 > 6 is not undefined, but it is a false value. In this case, a dualvar that acts like an empty string when used as a string, or 0 when used as a number.
Because the value is false, you can just do
if ( $wiz ) { ... }
If you really want $wiz to be undefined, you could do something like this.
my $wiz = 5 > 6 || undef;
Now, if the expression 5 > 6 is true, $wiz will be 1, otherwise it will be undef.
Boolean false and undef are two different things ...
comparison assignments are executed in Boolean context only returning boolean values only and undef is not boolean thing
chk this
if(defined false){
print "false is also defined thing.."
}else{
print "else .."
}
So back to question , seems like comparisons can't return undef

Perl Range Operator with Letters and Numbers

Is there a way to use Perl's range operator .. to use letters AND numbers?
For example, have:
for my $i ('X'..'9') {
print "$i ";
}
output X Y Z 1 2 3 4 5 6 7 8 9
Not unless for my $i ('X' .. 'Z', 1 .. 9) counts. "Z" will never increment to 1.

perl ternary statement has odd results with print

I've been messing around with writing FizzBuzz in perl...to be specific I want to make a nightmare looking line of code just to see if I can do it. This means nested ternary statements of course!
However I'm finding that on the 15's it never prints FizzBuzz, just Fizz. I cannot find a reason because that implies that if the first ternary statement returns true it's just skipping the second statement.
Here's the little nightmare I've come up with. Yes it can be way worse, but I'm really not that strong with perl and this is just an exercise for myself:
#!/usr/bin/perl
for (my $i=1; $i<=100; $i++) {
( !( ($i % 3 == 0) ? print "Fizz" : 0) && !( ($i % 5 == 0) ? print "Buzz" : 0) ) ? print "$i\n" : print "\n";
}
Here's the first 20 lines of output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
16
17
Fizz
19
Buzz
What would the print statement be doing that would cause this to happen?
This is an example of short-circuit evaluation, as documented in perlop. In cases of a() && b(), b() will never be evaluated if a() is false. In your (deeply nested and confusing) ternary statement, that amounts to the same thing. To fix this I'd split the statement up into multiple lines.
Simplified:
for my $i ( 1 .. 20 ) {
print +( ( $i % 3 ? '' : 'Fizz' ) . ( $i % 5 ? '' : 'Buzz' ) ) || $i, "\n";
}
Outputs:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Actually, here we go, bit shifting saves the day
#!/usr/bin/perl
for (my $i=1; $i<=100; $i++) {
(((($i % 3 == 0) ? print "Fizz" : 0) + ( ($i % 5 == 0) ? print "Buzz" : 0)) << 1) ? print "\n" : print "$i\n";
}
Output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
The most klugey solution

How can I square a number in Perl?

I have code to print out the first 9 squared numbers:
#!/usr/local/bin/perl
for($i=1;$i<10 ;$i++ )
{
printf $i^2 . "\n";
}
but for some reason this just outputs 30167451011. How do I properly square a number?
To square you have to use $i**2
#!/usr/local/bin/perl
for ( my $i = 1; $i < 10; $i++ ) {
print $i**2 . "\n";
}
This will output:
1
4
9
16
25
36
49
64
81
To explain what happened in the original code, you need to know 3 things: first, ^ is the XOR operator in perl, because it is the XOR operator in C.
1 ^ 2 = 3
2 ^ 2 = 0
3 ^ 2 = 1
4 ^ 2 = 6
...
Second, the ^ operator has lower precedence than the string concatenation operator . so $i^2 . "\n" is equivalent to $i ^ (2 . "\n")
Third, perl converts between strings and numbers as necessary. The . operator requires strings on both sides, so the 2 is converted to "2" and concatenated with the "\n" to become the string "2\n".
Then the ^ operator requires numbers on both sides, so the string "2\n" is converted to a number - by taking the leading number-looking portion and throwing away the rest. So the result of $i ^ 2 . "\n" is ultimately the same as $i ^ 2. Your "\n" didn't have any effect at all, so all the results are printed with nothing between them. 3, 0, 1, 6, ... became 3016...
^ is the Bitwise Xor operator
To square a number, you want the Exponentiation operator **
for my $i ( 1 .. 9 ) {
print $i**2, "\n";
}
Outputs:
1
4
9
16
25
36
49
64
81
foreach my $i (1..9){
say $i**2;
}
It can be achieve by doing this way also:
for (1..9) {
print $_*$_,"\n"
}
Output:
1
4
9
16
25
36
49
64
81
You can use the code below to get the squares.
print $_**2,"\n" for 1..10;

Condition in ternary operator doesn't cause any change

Here is my code sample:
for $i(1..100){
if ($i%15==0){$s="Divisible by 15"}
elsif($i%5==0){$s="Divisible by 5"}
else {$i%3==0 ? $s="Divisible by 3" : $s=$i};
print $s."\n"}
This displays partial correct results, if a number if divisible by 3, it displays the "number" and not "Divisible by 3".
Example of output:
1
2
3
4
Divisible by 5
6
7
8
9
Divisible by 5
PS: I have to write this code in the minimum no. of characters possible. (The reason why the code is so sluggish)
The fourth line is parsed as
(((($i % 3) == 0) ? ($s = 'Divisible by 3') : $s) = $i)
meaning that even when $i is divisible by 3, the assignment looks like
($s = 'Divisible by 3') = $i
Some fixes:
$i%3==0 ? ($s="Divisible by 3") : ($s=$i)
$s = ($i%3==0 ? "Divisible by 3" : $i)
Pro-tip: B::Deparse is very helpful for figuring this stuff out. I ran the command
perl -MO=Deparse,-p -e '$i%3==0 ? $s="Divisible by 3" : $s=$i'
to see exactly what the problem was.
The ternary ?: has higher precedence than assignment. Use parens to sort that out:
for my $i (1 .. 100) {
my $s = "Divisible by ";
$i%15==0 ? ($s .= 15)
: $i%5==0 ? ($s .= 5)
: $i%3==0 ? ($s .= 3)
: ($s =$i);
print "$s\n";
}
Output:
1
2
Divisible by 3
4
Divisible by 5
Divisible by 3
7
8
Divisible by 3
Divisible by 5
If you want a version with less characters,
for $i(1..100){print(($i%3&&$i%5?$i:"Divisible by ".($i%5?3:$i%15?5:15))."\n");}
Parenthesis after the print function means call the print function, not group the expression. Caused by the fact that parenthesis are optional for that function.