What does this mean in Perl 1..$#something? - perl

I have a loop for example :
for my $something ( #place[1..$#thing] ) {
}
I don't get this statement 1..$#thing
I know that # is for comments but my IDE doesn't color #thing as comment. Or is it really just a comment for someone to know that what is in "$" is "thing" ? And if it's a comment why was the rest of the line not commented out like ] ) { ?
If it has other meanings, i will like to know. Sorry if my question sounds odd, i am just new to perl and perplexed by such an expression.

The $# is the syntax for getting the highest index of the array in question, so $#thing is the highest index of the array #thing. This is documented in perldoc perldata
.. is the range operator, and 1 .. $#thing means a list of numbers, from 1 to whatever the highest index of #thing is.
Using this list inside array brackets with the # sigill denotes that this is an array slice, which is to say, a selected number of elements in the #place array.
So assuming the following:
my #thing = qw(foo bar baz);
my #place = qw(home work restaurant gym);
then #place[1 .. $#thing] (or 1 .. 2) would expand into the list work, restaurant.
It is correct that # is used for comments, but not in this case.

it's how you define a range. From starting value to some other value.
for my $something ( #place[1..3] ) {
# Takes the first three elements
}
Binary ".." is the range operator, which is really two different
operators depending on the context. In list context, it returns a list
of values counting (up by ones) from the left value to the right
value. If the left value is greater than the right value then it
returns the empty list. The range operator is useful for writing
foreach (1..10) loops and for doing slice operations on arrays. In the
current implementation, no temporary array is created when the range
operator is used as the expression in foreach loops, but older
versions of Perl might burn a lot of memory when you write something
like this:
http://perldoc.perl.org/perlop.html#Range-Operators

Related

When does -Contains not work and/or should I use something else

If I have two arrays of information. One massive and the other much smaller.
$lista ="1,2,3,4,5...etc for a lot "
$listb ="1,9,11" (Much smaller)
If ($listA -contains $listB){
Do stuff
}
Does this make sense and work?
It seems to me like it doesn't always work and I am having trouble double checking it.
Containment Operators are ideal for your use case, however, you're trying to compare an array against another array and that wouldn't work, these operators can help you identify if an element (a scalar, single value) exists in an array.
Both pairs (-in / -notin) and (-contains / -notcontains) should be equally efficient, the only difference is where the scalar is placed, for the first pair the scalar is on the left-hand side of the operation whereas for the later the scalar is on the right-hand side.
Taken from the Docs to helps us give some perspective to the explanation:
<Collection> -contains <Test-object>
<Collection> -notcontains <Test-object>
<Test-object> -in <Collection>
<Test-object> -notin <Collection>
As for your current code, that would require a loop, likely over the small array. Why?
These operators stop comparing as soon as they detect the first match, whereas the equality operators evaluate all input members. In a very large collection, these operators return quicker than the equality operators.
Following that logic, you could loop over your small array and add the comparison, this is up to you which one is more readable for you. Your code could look like this:
$lista = 1,2,3,4,5,6,7,8,9,10
$listb = 2,4,6,8,10
foreach($item in $listb)
{
if($item -in $lista) {
$item
# Do something here
}
}
There might be use cases where a HashSet<T> can be very useful, for example, if we want to get unique values out of $lista (in this case, we want case-insensitive hence the use of out of StringComparer.OrdinalIgnoreCase Property on it's constructor) and see which values are also contained on $listb, in this case probably we would want to use the .Contains(..) or .IntersectWith(..) or .ExceptWith(..) methods of this class instead of the operators.
$lista = 'A','x','y','z','a','B','b','C','c'
$listb = 'a','B','c'
$hashset = [System.Collections.Generic.HashSet[string]]::new(
[string[]]$lista,
[System.StringComparer]::OrdinalIgnoreCase
)
# To find the elements of `$listB` present in `$hashset`
foreach($item in $listb)
{
if($hashset.Contains($item)) {
$item
}
}
# The other way around, find the elements of `$hashset`
# present in `$listb`
$hashset.IntersectWith([string[]]$listb)
$hashset
# And the opposite, elements of `$hashset` NOT present in `$listb`
$hashset.ExceptWith([string[]]$listb)
$hashset

How does word counting by list assignment work in Perl?

I cannot exactly understand how the following snippet works:
my $str = 'abc def ghi';
my $num = () = $str =~ /\w+/g;
say $num; # prints the word count, 3
I know that $str =~ /\w+/g returns a list of the words which, apparently, is conveyed to the leftmost assignment. Then $num imposes a scalar context on that list and becomes 3.
But what does () = ('abc', 'def', 'ghi') mean? Is it something like my $a = my #b = (3, 5, 8)? If so, how is the list at the rightmost side transferred to $num at the leftmost side?
Each perl operator has specific behavior in list and scalar context. Operators give context to their operands, but receive context from what they are an operand to.
When a list assignment is placed in scalar context, it returns the number of elements on the right side of the assignment. This enables code like:
while (my #pair = splice(#array, 0, 1)) {
There's nothing special about how = () = is handled; you could just as well do = ($dummy) = or = (#dummy) =; the key part is that you want the match to be list context (producing all the possible matches) and then to just get a count of them.
So you do a list assignment (which is what = does whenever there's either a parenthesized expression or an array or slice as the left operand) but since you don't actually want the values, you can use an empty list. And then place that in scalar context; in this case, using the list assignment as the right operand to a scalar assignment.
Nowadays fewer people start learning Perl, one of reason is it has some obscure code like your example.
Check the perlsecret page for Saturn https://metacpan.org/pod/distribution/perlsecret/lib/perlsecret.pod#Goatse
=( )=
(Alternate nickname: "Saturn")
If you don't understand the name of this operator, consider yourself lucky. You are advised not to search the Internet for a visual explanation.
The goatse operator provides a list context to its right side and returns the number of elements to its left side. Note that the left side must provide a scalar context; obviously, a list context on the left side will receive the empty list in the middle.
The explanation is that a list assignment in scalar context returns the number of elements on the right-hand side of the assignment, no matter how many of those elements were actually assigned to variables. In this case, all the elements on the right are simply assigned to an empty list (and therefore discarded).

Perl's foreach with string argument

Perldocs only indicate that foreach loops "iterates over a normal list value" https://perldoc.perl.org/perlsyn.html#Foreach-Loops, but I sometimes see them with string arguments, such as the following examples:
foreach (`curl example.com 2>/dev/null`) {
# iterates 50 times
}
foreach ("foo\nbar\nbaz") {
# iterates just 1 time. Why?
}
Is the behavior of passing a string like this defined? Separately, why the disparate results from passing the string returned by a backticked command, and a literal string, as in the example?
In scalar context, backticks return a single scalar containing all the output of the enclosed command. But foreach (...) evaluates the backticks in list context, which will separates the output into a list with one line per element.
The question revolves around the context, a critical concept for many things in Perl.
The foreach loop needs a list to iterate over, so it imposes the list context to build the list values you saw mentioned in docs. The list may be formed with literals, qw(a b c), and may have one element; this is your second example, where one string is given, forming the one-element list that is iterated over.
The list can also come from an expression, that is evaluated in the list context; this is your first example. Many operations yield different returns based on context, and qx is such an operator as explained in mob's answer. This is something to note and be careful with. An expression may also return a single value regardless of context; then it is simply used to populate the list.
From perldoc -f qx:
In list context, returns a list of lines (however you've defined lines with $/ or $INPUT_RECORD_SEPARATOR), or an empty list if the command failed.
From perldoc perlsyn:
Compound statements
[...]
LABEL foreach VAR (LIST) BLOCK
A string is not a list. If you want to iterate over the characters in a string you'll need
foreach my $character (split('', "foo\nbar\nbaz")) {
I think you might be confusing Perl with Python:
>>> for c in "foo\nbar\nbaz":
... print c
...
f
o
.... remainder deleted ....
a
z
>>>
As pointed out by the other answers backticks/qx{} return a list of output lines from the executed command in list context.

How do I find the length of an associative array in AutoHotkey?

If you use the length() function on an associative array, it will return the "largest index" in use within the array. So, if you have any keys which are not integers, length() will not return the actual number of elements within your array. (And this could happen for other reasons as well.)
Is there a more useful version of length() for finding the length of an associative array?
Or do I need to actually cycle through and count each element? I'm not sure how I would do that without knowing all of the possible keys beforehand.
If you have a flat array, then Array.MaxIndex() will return the largest integer in the index. However this isn't always the best because AutoHotKey will allow you to have an array whose first index is not 1, so the MaxIndex() could be misleading.
Worse yet, if your object is an associative hashtable where the index may contain strings, then MaxIndex() will return null.
So it's probably best to count them.
DesiredDroids := object()
DesiredDroids["C3P0"] := "Gold"
DesiredDroids["R2D2"] := "Blue&White"
count :=0
for key, value in DesiredDroids
count++
MsgBox, % "We're looking for " . count . " droid" . ( count=1 ? "" : "s" ) . "."
Output
We're looking for 2 droids.

join() function is returning the type followed by a hex number instead of a concatenated string

In essence, I want to take an array and create a single string with the elements of said array separated by a newline.
I have an array named $zones. Outputting the reference to $zones confirms it's an array.
The following code:
print_log(Dumper($zones));
print_log('-------');
print_log(Dumper(join("\n",$zones)));
results in the following output
[2013-06-15 16:23:29 -0500] info [dnsadmin] $VAR1 = [
'fake.com25',
'fake.com2',
'fake.com27',
'fake.com43',
'fake.com41',
'fake.com40',
'fake.com39',
'fake.com37',
'fake.com36',
'fake.com35',
'fake.com31',
'fake.com56',
'fake.com55',
'fake.com54',
'fake.com53',
'fake.com52',
'fake.com51',
'fake.com50',
'fake.com49',
'fake.com48',
'fake.com42',
'fake.com38',
'fake.com34',
'fake.com33',
'fake.com32',
'fake.com30',
'fake.com29',
'fake.com28',
'fake.com26',
'fake.com24',
'fake.com23',
'fake.com69',
'fake.com68',
'fake.com67',
'fake.com66',
'0',
'fake.com44',
'fake.com45',
'fake.com46',
'fake.com278'
];
[2013-06-15 16:23:29 -0500] info [dnsadmin] -------
[2013-06-15 16:23:29 -0500] info [dnsadmin] $VAR1 = 'ARRAY(0x170cf0d8)';
I really don't want to loop over this array manually. Can someone explain why the join() function is returning the name of the type along with a hex number?
How to do is explained well by user1937198, but why it works this way?
It's simple:
$zones is not an array. It's an array reference.
join works on lists. So if you do:
join("\n",$zones)
You essentially are calling join on a single-element list. And the element is a reference, which happens to stringify to ARRAY(0x170cf0d8).
To make it work correctly you have to dereference it, and it is done by prefixing with real datatype (# or %, or in some cases $).
This can be written like this: #$zones, or (some, including me, say that it's more readable) as: #{ $zones }.
This is important when you're having nested structures, because while you can have plain array as a variable, when you're dealing with nested data structures, it's always references.
what you want is join("\n",#{$zones}))
$zones is array reference and to join array values you have to dereference it first by prefixing scalar $zones with #
print_log(Dumper(join("\n",#$zones)));
For more info there is short tutorial on references:
http://perldoc.perl.org/perlreftut.html#NAME