I want to create an array containing arrays of two numbers. Pretty straightforward. However, If I do not provide a leading comma before the first array, it is incorrect. Why is this leading comma required?
PS C:\src\powershell> Get-Content .\fr-btest.ps1
$files1 = #(
#(4, 1024)
, #((7), (16))
)
$files1
$files1.GetType()
$files1.Length
$files1.Count
'========'
$files2 = #(
, #(4, 1024)
, #((7), (16))
)
$files2
$files2.GetType()
$files2.Length
$files2.Count
PS C:\src\powershell> .\fr-btest.ps1
4
1024
7
16
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
3
3
========
4
1024
7
16
True True Object[] System.Array
2
2
#() is the array subexpression operator, which works differently than array construction operators you may be used to from other languages. The operator evaluates the nested subexpression and returns the output of that expression as an array. Meaning you can do something like this:
#(
Write-Output 'foo'
Get-Content 'C:\some\file.txt'
Test-Connection '192.168.23.42' -Count 1
)
and have an array come out.
For your first example this means that the two statements #(4, 1024) and , #((7), (16)) are evaluated individually, and the collective output of the two statements is then returned as an array.
The first statement (#(4, 1024)) outputs two integers, but the second statement (, #((7), (16))) outputs an array of two integers. That is because the leading comma in that statement is interpreted as the unary array construction operator (or comma operator), so you get an array nested in another array, and only the outer array is unrolled during output.
Essentially, your expression is the same as
$files1 = #(
4
1024
, #(7, 16)
)
or
$files1 = 4, 1024, #(7, 16)
Your second example avoids this pitfall, because both nested arrays are prepended with the unary array construction operator and thus protected from being completely unrolled.
With that said, I would recommend to define arrays in a more clear-cut way, e.g. like this:
$files1 = #(4, 1024),
#(7, 16)
or (using grouping expressions instead of array subexpressions) like this:
$files1 = (4, 1024),
(7, 16)
to avoid surprises like the one you observed. The outer #() isn't necessary for defining an array here. PowerShell automatically detects that via the trailing comma at the end of the first line.
For further information see about_Operators.
The key to understanding the Array subexpression operator #( ) is the realization that you don't need it to create arrays, instead arrays are created with the Comma operator ,
As a binary operator, the comma creates an array. As a unary operator,
the comma creates an array with one member. Place the comma before the member.
$myArray = 1,2,3
$SingleArray = ,1
$xs = (1,2,3), (4,5,6) # Count: 2
$ys = (1,2,3),
(4,5,6) # Count: 2
Now consider
# A - two expressions, each expression yields one array of size 3
(1,2,3)
(4,5,6)
# B - one expression resulting in an array of two elements
(1,2,3),
(4,5,6)
# C - similar to A except the sizes are 3 and 1
# (the second array contains a single element)
(1,2,3)
,(4,5,6)
And the final step is to realize that
in essence, the #(...) operation is syntactic sugar for
[array] $(...)
as explained by the PowerShell Team Blog (The link was given by Christopher G. Lewis answer). Although the meaning and limitations of in essence is not entirely clear to me.
Powershell uses both a comma and a line break as an array separator. Your first declare:
$files1 = #(
#(4, 1024)
, #((7), (16))
)
Creates the following:
$files1[0] = 4
$files1[1] = 1024
$files1[2] = #(7,16)
Your second declare
$files1 = #(
, #(4, 1024)
, #((7), (16))
)
Creates the following:
$files1[0] = #(4, 1024)
$files1[1] = #(7, 16)
As to the parsing decision, it is dependent on the first non-white space character encountered on a line:
Array Literals In PowerShell
and
Understanding PowerShell Parsing Modes
Related
I'm pretty new to Perl and this is my most complex project yet. Apologies if any parts of my explanation don't make sense or I miss something out - I'll be happy to provide further clarification. It's only one line of code that's causing me an issue.
The Aim:
I have a text file that contains a single column of data. It reads like this:
0
a,a,b,a
b,b,b,a
1
a,b,b,a
b,b,b,a
It continues like this with a number in ascending order up to 15, and the following two lines after each number are a combination of four a's or b's separated by commas. I have tied this file to an array #diplo so I can specify specific lines of it.
I also have got a file that contains two columns of data with headers that I have converted into a hash of arrays (with each of the two columns being an array). The name of the hash is $lookup and the array names are the names of the headings. The actual arrays only start from the first value in each column that isn't a heading. This file looks like this:
haplo frequency
"|5,a,b,a,a|" 0.202493719
"|2,b,b,b,a|" 0.161139191
"|3,b,b,b,a|" 0.132602458
This file contains all of the possible combinations of a or b at the four positions combined with all numbers 0-14 and their associated frequencies. In other words, it includes all possible combinations from "|0,a,a,a,a|" followed be "|1,a,a,a,a|" through to "|13,b,b,b,b|" and "|14,b,b,b,b|".
I want my Perl code to go through each of the combinations of letters in #diplo starting with a,a,b,a and record the frequency associated with the row of the haplo array containing each number from 0-14, e.g. first recording the frequency associated with "|0,a,a,b,a|" then "|1,a,a,b,a|" etc.
The output would hopefully look like this:
0 #this is the number in the #diplo file and they increase in order from 0 up to 15
0.011 0.0023 0.003 0.0532 0.163 0.3421 0.128 0.0972 0.0869 0.05514 0.0219 0.0172 0.00824 0.00886 0.00196 #these are the frequencies associated with x,a,a,b,a where x is any number from 0 to 14.
My code:
And here is the Perl code I created to hopefully sort this out (there is more to create the arrays and such which I can post if required, but I didn't want to post a load of code if it isn't necessary):
my $irow = 1; #this is the row/element number in #diplo
my $lrow = 0; #this is the row/element in $lookup{'haplo'}
my $copynumber = 0;
#print "$copynumber, $diplo[2]";
while ($irow < $diplolines - 1) {
while ($copynumber < 15) {
while ($lrow < $uplines - 1) {
if ("|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]) { ##this is the only line that causes errors
if ($copynumber == 0) {
print "$diplo[$irow-1]\n";
#print "$lookup{'frequency'}[$lrow]\t";
}
print "$lookup{'frequency'}[$lrow]\t";
}
$lrow = $lrow + 1;
}
$lrow = 0;
$copynumber = $copynumber + 1;
}
$lrow = 0;
$copynumber = 0;
$irow = $irow + 1;
}
However, the line if ("|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]) is causing an error Can't modify string in scalar assignment near "]) ".
I have tried adding in speech marks, rounded brackets and apostrophes around various elements in this line but I still get some sort of variant on this error. I'm not sure how to get around this error.
Apologies for the long question, any help would be appreciated.
EDIT: Thanks for the suggestions regarding eq, it gets rid of the error and I now know a bit more about Perl than I did. However, even though I don't get an error now, if I put anything inside the if loop for this line, e.g. printing a number, it doesn't get executed. If I put the same command within the while loop but outside of the if, it does get executed. I have strict and warnings on. Any ideas?
= is assignment, == is numerical comparison, eq is string comparison.
You can't modify a string:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" = "abcdef") { print "match\n"; } '
Found = in conditional, should be == at -e line 1.
Can't modify string in scalar assignment at -e line 1, near ""abcdef") "
Execution of -e aborted due to compilation errors.
Nonnumerical strings act like zeroes in a numerical comparison:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" == 0) { print "match\n"; } '
Argument "abcdef" isn't numeric in numeric eq (==) at -e line 1.
match
A string comparison is probably what you want:
$ perl -e 'use strict; use warnings; my $foo="def";
if ("abc$foo" eq "abcdef") { print "match\n"; } '
match
This is the problematic expression:
"|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]
The equals sign (=) is an assignment operator. It assigns the value on its right-hand side to the variable on its left-hand side. Therefore, the left-hand operand needs to be a variable, not a string as you have here.
I don't think you want to do an assignment here at all. I think you're trying to check for equality. So don't use an assignment operator, use a comparison operator.
Perl has two equality comparison operators. == does a numeric comparison to see if its operands are equal and eq does a string comparison. Why does Perl need two operators? Well Perl converts automatically between strings and numbers so it can't possibly know what kind of comparison you want to do. So you need to tell it.
What's the difference between the two types of comparison? Well, consider this code.
$x = '0';
$y = '0.0';
Are $x and $y equal? Well it depends on the kind of comparison you do. If you compare them as numbers then, yes, they are the same value (zero is the same thing whether it's an integer or a real number). But if you compare them as strings, they are different (they're not the same length for a start).
So we now know the following
$x == $y # this is true as it's a numeric comparison
$x eq $y # this is false as it's a string comparison
So let's go back to your code:
"|$copynumber,$diplo[$irow]|" = $lookup{'haplo'}[$lrow]
I guess you started with == here.
"|$copynumber,$diplo[$irow]|" == $lookup{'haplo'}[$lrow]
But that's not right as |$copynumber,$diplo[$irow]| is clearly as string, not a number. And Perl will give you a warning if you try to do a numeric comparison using a value that doesn't look like a number.
So you changed it to = and that doesn't work either as you've now changed it to an assignment.
What you really need is a string comparison:
"|$copynumber,$diplo[$irow]|" eq $lookup{'haplo'}[$lrow]
I have a powershell array with about 10 items. I'm trying to take the 1st element (0) of the array and move it to the last item. There will still be (only) 10 items in the array at the end.
If you don't mind creating a new array, here's a concise solution:
$a = 1..10 # sample array: 1, 2, ..., 10
$a = $a[1..($a.count-1)] + $a[0]
minmaxavg eventually came up with an even more concise alternative in their answer:
$a[1..($a.count-1) + 0]
Performance-wise the two solutions are virtually identical.
Also, their [array]::Copy() solution is a more efficient, in-place alternative - at the expense of being more complex to write; it could be simplified somewhat to:
$first = $a[0]; [array]::Copy($a, 1, $a, 0, $a.Count - 1); $a[-1] = $first
$a now contains:
2
3
4
5
6
7
8
9
10
1
$a[1..($a.count-1)] returns a new array that contains all elements of $a starting with the element at index 1, i.e., starting with the 2nd element.
1..($a.count-1) is a range expression which itself creates an array - the array of indices to extract from the input array.
As an aside:
PowerShell supports index -1 to refer to an array's last element, -2 to refer to the second-to-last, and so on.
However, using negative indices in a range may not do what you expect; e.g., 1..-1 does not extract all elements from the 2nd to the second-to-last one (GitHub issue #7940 proposes new syntax similar to C#'s ranges to support this use case); instead, it extracts elements with indices 1, 0, and -1 (the last), because these are the elements of the array (of indices) that the range operator (..) generates.
+ $a[0] "appends" the first element to that new array; technically, this creates another array that is the concatenation of the elements of the LHS and the RHS.
In summary, 3 arrays were created in the process: 2 auxiliary, transient ones behind the scenes, plus the result array assigned back to $a.
Therefore, while using the range operator (..) to extract array slices and using + to concatenate arrays is very convenient, it is not efficient, though with small arrays that usually doesn't matter - beware of "appending" to an array in a loop with +=, however, as you'll be recreating the array in every iteration.
If you want to rotate the array in-place, this is more efficient than manually iterating and shifting elements in Powershell (.NET Array.Copy is analogous to Java System.arraycopy):
New-Variable -Name first -Value $arr[0] -Scope Private
[array]::Copy($arr, 1, $arr, 0, $arr.Length - 1)
$arr[$arr.Length - 1] = $Private:first
If copying is desirable (10 elements should be ok):
$arr = $arr[1 .. ($arr.Length - 1) + 0]
Powershell allows combination of two or more ranges through the + operator. Especially if you intend the array to be immutable, this should be the most optimal solution.
I'm having some trouble wrapping my head around perl's order of operations. I have the following:
>> my $n = 2;
>> my #arr = (1,2,3,4);
>> print $n/scalar #arr * 100;
0.005
But adding parens:
>> my $n = 2;
>> my #arr = (1,2,3,4);
>> print $n/(scalar #arr) * 100;
50
Looking at the order of operations, it seems as though the first thing that should happen are list operations. In this case, the first one encountered would be scalar #arr, which should return 4. The resulting expression should be print $n/4 * 100, which would follow a standard order of operations and produce 50.
But instead, I assume what is happening is it is performing #arr * 100 first, which produces the scalar value 400, then executed scalar 400, which produces 400, then executes $n/400, giving 0.005.
If the latter is what is happening, then my question is where does scalar fall in the order of operations. If something else is going on, then my question is, well, what?
You can see how Perl parses the code by running it through B::Deparse with -p:
perl -MO=Deparse,-p script.pl
I tried 3 different ways:
print $n/scalar #arr * 100;
print $n/(scalar #arr) * 100;
print $n/#arr * 100;
This was the output:
print(($n / scalar((#arr * 100))));
print((($n / scalar(#arr)) * 100));
print((($n / #arr) * 100));
* is higher than the "named unary operators" (where scalar belongs, check the link) in the precedence table in perlop.
From perlop documentation
In the absence of parentheses, the precedence of list operators such
as print, sort, or chmod is either very high or very low depending on
whether you are looking at the left side or the right side of the
operator. For example, in
#ary = (1, 3, sort 4, 2);
print #ary; # prints 1324
the commas on the right of the sort are evaluated before the sort, but the commas on the left are evaluated after. In other words, list operators tend to gobble up all the arguments that follow them, and then act like a simple TERM with regard to the preceding expression.
And * operator has a higher precedence 7. than named unary operator 10.
so in scalar #arr * 100 * has higher precedence.
Example1:
Note 2: The comma is also used so
separate items in an array {0,-30}
Example2:
To create an array, we create a
variable and assign the array. Arrays
are noted by the “#” symbol. Let’s
take the discussion above and use an
array to connect to multiple remote
computers: $strComputers =
#(“Server1″, “Server2″, “Server3″)
So, which one is correct or what is the difference ?
Example 2 uses the array cast syntax which allows a single element, for example, to be treated as an array:
$myList = #("Hello")
Essentially, it allows anything between the parenthesis to be treated as an array including the output from other commands:
$myArray = #(Get-Process Excel)
Alternatively you can just create an array by specifying a comma separated list:
$myArray = "hello", "world", "again"
(The curly brackets are not needed)
You can also attain a single element array by prepending the , operator to a single value:
[PS] C:\>$a = ,"Hello World"
[PS] C:\>$a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
[PS] C:\>$a.count
1
Why does the code below return 11 with this :- #myarray = ("Rohan");
Explaination i got was :- The expression $scalar x $num_times, on the other hand, returns a string containing $num_times copies of $scalar concatenated together string-wise.
So it should give 10 not 11 ...
code is as below :-
print "test:\n";
#myarray = ("Rohan"); # this returns 11
###myarray = ("Rohan","G"); this returns 22
#myarray2 = (#myarray x 2);
#myarray3 = ((#myarray) x 2); #returns Rohan,Rohan and is correct
print join(",",#myarray2,"\n\n");
print join(",",#myarray3,"\n\n");
What’s happening is that the x operator supplies scalar context not just to its right‐hand operand, but also to its left‐and operand as well — unless the LHO is surrounded by literal parens.
This rule is due to backwards compatibility with super‐ancient Perl code from back when Perl didn’t understand having a list as the LHO at all. This might be a v1 vs v2 thing, a v2 vs v3 thing, or maybe v3 vs v4. Can’t quite remember; it was a very long time ago, though. Ancient legacy.
Since an array of N elements in scalar context in N, so in your scenario that makes N == 1 and "1" x 2 eq "11".
Perl is doing exactly what you asked. In the first example, the array is in scalar context and returns its length. this is then concatenated with itself twice. In the second example, you have the array in list context and the x operator repeats the list.