How does Perl decide which order to evaluate terms in an expression? - perl

Given the code:
my $x = 1;
$x = $x * 5 * ($x += 5);
I would expect $x to be 180:
$x = $x * 5 * ($x += 5); #$x = 1
$x = $x * 5 * 6; #$x = 6
$x = 30 * 6;
$x = 180;
180;
But instead it is 30; however, if I change the ordering of the terms:
$x = ($x += 5) * $x * 5;
I do get 180. The reason I am confused is that perldoc perlop says very plainly:
A TERM has the highest precedence in Perl. They include variables,
quote and quote-like operators, any expression in parentheses, and any
function whose arguments are parenthesized.
Since ($x += 5) is in parentheses, it should be a term, and therefore executed first, regardless of the ordering of the expression.

The act of typing out the question yielded the answer to me: terms have the highest precedence. That means that the $x in the first chunk of code is evaluated and yields 1, then 5 is evaluated and yields 5, then ($x += 5) is evaluate and yields 6 (with a side-effect of setting $x to 6):
$x = $x * 5 * ($x += 5);
address of $x = $x * 5 * ($x += 5); #evaluate $x as an lvalue
address of $x = 1 * 5 * ($x += 5); #evaluate $x as an rvalue
address of $x = 1 * 5 * ($x += 5); #evaluate 5
address of $x = 1 * 5 * 6; #evaluate ($x += 5), $x is now 6
address of $x = 1 * 5 * 6; #evaluate 1 * 5
address of $x = 5 * 6; #evaluate 1 * 5
address of $x = 30; #evaluate 5 * 6
30; #evaluate address of $x = 30
Similarly, the second example reduces like this:
$x = ($x += 5) * $x * 5;
address of $x = ($x += 5) * $x * 5; #evaluate $x as an lvalue
address of $x = 6 * $x * 5; #evaluate ($x += 5), $x is now 6
address of $x = 6 * 6 * 5; #evaluate $x as an rvalue
address of $x = 6 * 6 * 5; #evaluate 5
address of $x = 36 * 5; #evaluate 6 * 6
address of $x = 180; #evaluate 36 * 5
180; #evaluate $x = 180

Whenever I have confusion about stuff like this I first pull out perldoc perlop, and then if I'm still not sure, or want to see how a particular block of code will get executed, I use B::Deparse:
perl -MO=Deparse,-p,-q,-sC
my $x = 1;
$x = $x * 5 * ($x += 5);
^D
gives:
(my $x = 1);
($x = (($x * 5) * ($x += 5)));
- syntax OK
So substituting values at each stage gives:
($x = (($x * 5) * ($x += 5)));
($x = ((1 * 5) * ($x += 5)));
($x = ((5) * (6))); # and side-effect: $x is now 6
($x = (5 * 6));
($x = (30));
($x = 30);
$x = 30;
So the fact that $x was temporarily set to 6 doesn't really affect anything, because the earlier value (1) was already substituted into the expression, and by the end of the expression it is now 30.

$x by itself is also a TERM. Since it is encountered first (in your first example), it is evaluated first.

The associativity of the * operator is leftward, so the left most term is always evaluated before the right most term. Other operators, such as ** are right associative and would have evaluated ($x += 5) before the rest of the statement.

Related

Windows Powershell equivalent to this Linux command that calculates a number to 10000 decimal places?

I have seen this fun little command going around on social media, which outputs pi to 10000 decimal places. I'm wondering what is the Windows Powershell equivalent to this?
echo "scale=10000; 4*a(1)" | bc -l
I know I can use the Powershell Math library to do the basic math formula, but how to set the number of decimal places to 10000?
PS C:\Users\Me> [Math]::Atan(1)*4
3.14159265358979
The [Math] class only works with double so obviously you can't get more than 53 bits of precision. A few of its methods also support decimal so you'll get a little bit more digits but obviously no way near 10000 digits. There's no arbitrary precision floating point type in .NET so you're completely on your own to calculate the digits if you don't want to retrieve them from some storage space
There's BigInteger (PowerShell type accelerator name: [bigint]) though, which helps a lot since you won't need to do arbitrary precision math yourself. For example to calculate 10000 digits of π you can calculate 10000π and does the operations in integer or fixed point math
There are many algorithms to do that such as this one: How does this code calculate pi with high precision?
Fortunately Rosetta Code has a sample snippet to calculate π. After the below function has been declared just call Get-Pi 10000 to get the desired output
Function Get-Pi ( $Digits )
{
$Big = [bigint[]](0..10)
$ndigits = 0
$Output = ""
$q = $t = $k = $Big[1]
$r = $Big[0]
$l = $n = $Big[3]
# Calculate first digit
$nr = ( $Big[2] * $q + $r ) * $l
$nn = ( $q * ( $Big[7] * $k + $Big[2] ) + $r * $l ) / ( $t * $l )
$q *= $k
$t *= $l
$l += $Big[2]
$k = $k + $Big[1]
$n = $nn
$r = $nr
$Output += [string]$n + '.'
$ndigits++
$nr = $Big[10] * ( $r - $n * $t )
$n = ( ( $Big[10] * ( 3 * $q + $r ) ) / $t ) - 10 * $n
$q *= $Big[10]
$r = $nr
While ( $ndigits -lt $Digits )
{
While ( $ndigits % 100 -ne 0 -or -not $Output )
{
If ( $Big[4] * $q + $r - $t -lt $n * $t )
{
$Output += [string]$n
$ndigits++
$nr = $Big[10] * ( $r - $n * $t )
$n = ( ( $Big[10] * ( 3 * $q + $r ) ) / $t ) - 10 * $n
$q *= $Big[10]
$r = $nr
}
Else
{
$nr = ( $Big[2] * $q + $r ) * $l
$nn = ( $q * ( $Big[7] * $k + $Big[2] ) + $r * $l ) / ( $t * $l )
$q *= $k
$t *= $l
$l += $Big[2]
$k = $k + $Big[1]
$n = $nn
$r = $nr
}
}
$Output
$Output = ""
}
}

Issues with arithmetic-operators

my problem is that I'm trying to put a simple math formula in a PowerShell script but this "arithmetic-operators" is an issue now for me, It was used to work this way, but something changed and now doesn't matter what I put in it multiples for more numbers as if they were letters, (it's only stacking all of them together)
I even tried fixing it using
$x=[int]$xx
to fix my variables so PowerShell could understand, and it did work just not with broken numbers Ex: 7.5 or 3.1 or 9.6 No broken numbers. Can anyone help me
$pi=[math]::pi
$xx= Read-Host -prompt "X "
$yy= Read-Host -prompt "Y "
$zz= Read-Host -prompt "Z "
$x=[int]$xx
$y=[int]$yy
$z=[int]$zz
$re = $z * $y
$r = $z * $x + $y * $x + $z * $x + $y * $x
$res = 2 * ($re) + $r
echo .
echo "$r = $z * $x + $y * $x + $z * $x + $y * $x"
echo .
echo "$re = $z * $y"
echo .
echo "$res = 2 * ($re) + $r"
echo .
echo "Total = $res"
echo .
pause
if you run this and put X as 27, Y as 7.5 and Z as 17, your answer should be 1578, and you fixed it
You are getting the wrong answer because 7.5 is not an [int]. It is rounding 7.5 to 8 to cast it to an int. You need $y=[single]$yy to make this work or any other type that supports decimals. I would replace all [int] with [single] if expect decimal values. See the following:
$pi=[math]::pi
$xx= Read-Host -prompt "X "
$yy= Read-Host -prompt "Y "
$zz= Read-Host -prompt "Z "
$x=[single]$xx
$y=[single]$yy
$z=[single]$zz
$re = $z * $y
$r = $z * $x + $y * $x + $z * $x + $y * $x
$res = 2 * ($re) + $r
echo "$r = $z * $x + $y * $x + $z * $x + $y * $x"
echo "$re = $z * $y"
echo "$res = 2 * ($re) + $r"
echo "Total = $res"
Output of the variables above:
$x,$y,$z,$re,$r,$res
27
7.5
17
127.5
1323
1578
Other types that you could potentially use are [double], which is the default type of uncasted numbers with decimals, and [decimal]. You can also use the -as type operator like $y = $yy -as [double]. See About Type Operators

It there a way in powershell to make a positive number to a negative number whitout using multiplication?

I was wondering if there is a way to make a positive number into a negative number whitout using a multiplication like $b = $a * -1
I'm looking for the most cost sensible way because I'm gonna do this a lot of times in a script.
-edit
At this point I'm using this, but lookes very costly computation wise:
$temp_array = New-Object 'object[,]' $this.row,$this.col
for ($i=0;$i -le $this.row -1 ; $i++) {
for ($j=0;$j -le $this.col -1 ; $j++) {
$digit = $this.data[$i,$j] * -1
$temp_array[$i,$j] = 1 / ( 1 + [math]::exp( $digit ) )
#[math]::Round( $digit ,3)
}
}
$this.data = $temp_array
To unconditionally turn a positive number into its negative equivalent (or, more generally, flip a number's sign), simply use the unary - operator:
PS> $v = 10; -$v
-10
Applied to your case:
$digit = -$this.data[$i,$j]
As an aside: If performance matters, you can speed up your loops by using .., the range operator to create the indices to iterate over:
$temp_array = New-Object 'object[,]' $this.row,$this.col
for ($i in 0..($this.row-1)) {
for ($j in 0..($this.col-1)) {
$digit = - $this.data[$i,$j]
$temp_array[$i,$j] = 1 / ( 1 + [math]::exp( $digit ) )
}
}
$this.data = $temp_array

switch - multiple variables

I'm trying to create a script which will work assign different IP ranges depending on the choice, currently I've done something like this:
# Get DHCP Scope Start - first IP to check - End Last IP to check
$X = 0
$Y = 0
$Z = 0
$End = 0
$DHCPServer = "DHCP"
$ScopeID = "10.0.0.0"
switch (Read-Host "Choose device to add: 1 PS3,2 PS4,3 PS4Pro,4 XboxOne,") {
1 {$z = 1 $End = 20}
2 {$z = 30 $End = 50}
3 {$z = 100 $End = 255}
4 {$y = 1 $z = 1 $end = 100}
}
But no matter how I type arguments, PowerShell always return the errors like
Unexpected token $end in expression or statement
Is there a way to fix it?
Adding some semicolons should do the trick. Alternatively, you could use new lines.
# Get DHCP Scope Start - first IP to check - End Last IP to check
$X = 0
$Y = 0
$Z = 0
$End = 0
$DHCPServer = "DHCP"
$ScopeID = "10.0.0.0"
switch (Read-Host "Choose device to add: 1 PS3,2 PS4,3 PS4Pro,4 XboxOne,") {
1 {$z = 1; $End = 20}
2 {$z = 30; $End = 50}
3 {$z = 100; $End = 255}
4 {$y = 1; $z = 1; $end = 100}
}

Powershell: HSL to RGB

I need to convert HSL color values to RGB, or to be more precise HSL-values to a System.Drawing.Color object with Powershell.
There are a few solutions in other prog.-languages out there (like LINK). But while it looks simple, I dont get it converted it into Powershell.
Function HSLtoRGB ($H,$S,$L) {
$H = [double]($H / 360)
$S = [double]($S / 100)
$L = [double]($L / 100)
if ($s -eq 0) {
$r = $g = $b = $l
}
else {
if ($l -lt 0.5){
$q = $l * (1 + $s)
}
else {
$q = $l + $s - $l * $s
}
$p = (2 * $L) - $q
$r = (Hue2rgb $p $q ($h + 1/3))
$g = (Hue2rgb $p $q $h )
$b = (Hue2rgb $p $q ($h - 1/3))
}
$r = [Math]::Round($r * 255)
$g = [Math]::Round($g * 255)
$b = [Math]::Round($b * 255)
return ($r,$g,$b)
}
function Hue2rgb ($p, $q, $t) {
if ($t -lt 0) { $t++ }
if ($t -gt 0) { $t-- }
if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) }
if ($t -lt 1/2) { return $q }
if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
return $p
}
HSLtoRGB 63 45 40 # result should be R 145 G 148 B 56
Let's start with the line you're having trouble with translating:
$q = l < 0.5 ? l * (1 + s) : l + s - l * s; #could not translate this line
This construct:
statement ? someValue : anotherValue;
is known as a ternary operation. It basically means:
if(statement){
someValue
} else {
anotherValue
}
So in PowerShell that becomes:
$q = if($l -lt 0.5){
$l * (1 + $s)
} else {
$l + $s - $l * $s
}
Your translation of the inline Hue2Rgb function has two typos that greatly change the calculation:
function Hue2rgb ($p, $q, $t) {
if ($t -lt 0) { $t++ }
if ($t -gt 0) { $t-- } # This condition should be ($t -gt 1)
if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) } # The innermost calculation should be ($q - $p) not ($q + $p)
if ($t -lt 1/2) { return $q }
if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
return $p
}
Regarding the input values, if you take a look at the comments in the original script:
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
So if you want to pass your input values as degrees (hue) and percentages (saturation + luminance), you'll have to handle a conversion to a relative value between 0 and 1:
Function HSLtoRGB ($H,$S,$L) {
$H = [double]($H / 360)
$S = [double]($S / 100)
$L = [double]($L / 100)
# rest of script
}
Lastly, you can use Color.FromArgb() to return an actual Color object:
$r = [Math]::Round($r * 255)
$g = [Math]::Round($g * 255)
$b = [Math]::Round($b * 255)
return [System.Drawing.Color]:FromArgb($r,$g,$b)