Related
sub a { my #c = 1, 2, 3, 4; return #c };
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say a().WHAT; # (Array)
say b().WHAT; # (Array)
my #e = a();
my #f = b();
say #e; # [1 2 3 4]
say #e[1]; # 2
say #f; # [[1 2 3 4]]
say #f[1]; # (Any)
# valid raku code, no errors messages
These subroutines both return an array, but the returned arrays behave differently. What would be the correct expression to make clear in the documentation which array type is returned by the subroutine?
sub b ( --> Array ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT; # (Array)
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;
# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
# in sub b at ...
# in block <unit> at ...
This is all about a Scalar that hasn't decontainerized.
You could change return $d to return #$d
One option to get the same behavior is to alter the b routine.
You have written "These subroutines both return an array, but the returned arrays behave differently.". But, as Holli notes, b instead returns the Scalar bound to $d (that in turn contains an array):
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)
You could change that by "decontainerizing" $d, eg by prepending #:
sub b { my $d = [ 1, 2, 3, 4 ]; return #$d };
say b().VAR.WHAT; # (Array)
my #f = b();
say #f; # [1 2 3 4]
say #f[1]; # 2
Or change #f = b() to #f = b[]
If you don't decontainerize the value returned by b, then a second issue / opportunity arises.
Regardless of what b returns, the #f assignment will evaluate the list of values being assigned to it. In a list context, Scalars are left as is (just as they were with a plain return $d). So if you do not change b to decontainerize, then you'll need to do so in the assignment to #f instead, if you want #e and #f to end up the same.
This time you can't just prepend # to do so. Because that would spell #b -- which Raku would interpret as an #b variable.
One option is to write #f = #(b()), but that would be ugly / non-idiomatic. Another option is to write #f = b[]. This takes advantage of the fact that the parens in the b calls were redundant. Appending [] (a "zen slice") has the same effect as writing #(b), but is one character less.
So, to decontainerize in the list assignment, you could write:
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)
my #f = b[];
say #f; # [1 2 3 4]
say #f[1]; # 2
"to make clear in the documentation"
What would be the correct expression to make clear in the documentation which array type what is returned by the subroutine?
I'm not sure what you mean by this question, even with the switch to merely "what is returned".
I'm also not sure what to point you to in the doc, and even if there is anywhere good to point you to, relative to the scenario in your SO.
I do know that, if it were me, I'd find the following doc sections confusing, relative to your scenario:
The Scalar containers and listy things section Holli linked. That section seems to me to currently be about use of Scalar containers inside lists/arrays, which is relevant for the second issue I wrote about above ($d is in a list on the rhs of the assignment to #f). But that's not relevant to the first issue I wrote about (return $d from the b routine). There things are the other way around, namely there's an array inside a Scalar.
The Scalar containers section earlier on the same page. The opening sentence -- "Although objects of type Scalar are everywhere in Raku, you rarely see them directly as objects, because most operations decontainerize ..." works for me. But "a routine can return a container if it is marked as is rw" is more problematic. It is true:
my $x = 23;
sub f() is rw { $x };
f() = 42;
say $x; # OUTPUT: «42»
But one doesn't have to mark a routine is rw to return a container. One can use the return routine, as you did:
my $x = 23;
sub f() { return $x };
say f().VAR.WHAT; # OUTPUT: «Scalar»
The original question asks why the following error occurs:
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;
# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
# in sub b at ...
# in block <unit> at ...
This answer is intended to elaborate on constraints of the return type as documented here
If you do this, now you are returning a Scalar:
sub b( --> Scalar ) { my $x=42; my $d = $x; return $d };
say b().WHAT;
You get a different error
Type check failed for return value; expected Scalar but got Int (42)
in sub b at scum.raku line 2
in block <unit> at scum.raku line 7
What is going on here?
Well raku automatically decontainerizes the Scalar $x before testing the type - this is how Scalar containers work. So run of the mill code you should be using concrete return type constraints like Int, Real, Num, IntStr, Allomorph, Cool and so on. Raku built in types are beautifully crafted to give the coder precise control of the level of this test.
BUT - if you want to do power tricks, then you can apply the --> Scalar constraint like this (as mentioned by #raiph):
sub b( --> Scalar ) { my $x=42; my $d = $x.VAR; return $d };
say b().WHAT; #(Scalar)
In other words, the meta method .VAR bypasses the normal transparent decont of Scalar containers to let you test whether or not your return type is top level a container.
So (as I am sure you have gathered from raiph's better answer) please document --> Scalar as a rare form as opposed to the common --> Array check.
To select multiple elements from an array in perl6, it is easy: just use a list of indices:
> my #a = < a b c d e f g >;
> #a[ 1,3,5 ]
(b d f)
But to de-select those elements, I had to use Set:
> say #a[ (#a.keys.Set (-) (1,3,5)).keys.sort ]
(a c e g)
I am wondering if there is an easier way because the arrays I use are often quite large?
sub infix:<not-at> ($elems, #not-ats) {
my $at = 0;
flat gather for #not-ats -> $not-at {
when $at < $not-at { take $at++ xx $not-at - $at }
NEXT { $at++ }
LAST { take $at++ xx $elems - $not-at - 1 }
}
}
my #a = < a b c d e f g >;
say #a[ * not-at (1, 3, 5) ]; # (a c e g)
I think the operator code is self-explanatory if you know each of the P6 constructs it uses. If anyone would appreciate an explanation of it beyond the following, let me know in the comments.
I'll start with the two aspects that generate the call to not-at.
* aka Whatever
From the Whatever doc page:
When * is used in term position, that is, as an operand, in combination with most operators, the compiler will transform the expression into a closure of type WhateverCode
* is indeed used in the above as an operand. In this case it's the left argument (corresponding to the $elems parameter) of the infix not-at operator that I've just created.
The next question is, will the compiler do the transform? The compiler decides based on whether the operator has an explicit * as the parameter corresponding to the * argument. If I'd written * instead of $elems then that would have made not-at one of the few operators that wants to directly handle the * and do whatever it chooses to do and the compiler would directly call it. But I didn't. I wrote $elems. So the compiler does the transform I'll describe next.
The transform builds a new WhateverCode around the enclosing expression and rewrites the Whatever as "it" aka the topic aka $_ instead. So in this case it turns this:
* not-at (1,3,5)
into this:
{ $_ not-at (1,3,5) }
What [...] as a subscript does
The [...] in #a[...] is a Positional (array/list) subscript. This imposes several evaluation aspects, of which two matter here:
"it" aka the topic aka $_ is set to the length of the list/array.
If the content of the subscript is a Callable it gets called. The WhateverCode generated as explained above is indeed a Callable so it gets called.
So this:
#a[ * not-at (1,3,5) ]
becomes this:
#a[ { $_ not-at [1,3,5] } ]
which turns into this:
#a[ { infix:not-at(7, [1,3,5]) } ]
Given the indexer wants the elements to extract, we could solve this by turning the list of elements to exclude into a list of ranges of elements to extract. That is, given:
1, 3, 5
We'd produce something equivalent to:
0..0, 2..2, 4..4, 6..Inf
Given:
my #exclude = 1, 3, 5;
We can do:
-1, |#exclude Z^..^ |#exclude, Inf
To break it down, zip (-1, 1, 3, 5) with (1, 3, 5, Inf), but using the range operator with exclusive endpoints. This results in, for the given example:
(-1^..^1 1^..^3 3^..^5 5^..^Inf)
Which is equivalent to the ranges I mentioned above. Then we stick this into the indexer:
my #a = <a b c d e f g>
my #exclude = 1, 3, 5;
say #a[-1, |#exclude Z^..^ |#exclude, Inf].flat
Which gives the desired result:
(a c e g)
This approach is O(n + m). It will probably work out quite well if there array is long, but the number of things to exclude is comparatively small, since it only produces the Range objects needed for the indexing, and then indexing by range is relatively well optimized.
Finally, should the flat on the outside be considered troublesome, it's also possible to move it inside:
#a[{ flat -1, |#exclude Z^..^ |#exclude, $_ }]
Which works because the block is passed the number of elements in #a.
Here's another option:
my #a = < a b c d e f g >;
say #a[#a.keys.grep(none(1, 3, 5))];
But all in all, arrays aren't optimized for this use case. They are optimized for working with a single element, or all elements, and slices provide a shortcut for (positively) selecting several elements by key.
If you tell us about the underlying use case, maybe we can recommend a more suitable data structure.
This might be slow for big arrays, but it's logically the closer to what you're looking for:
my #a = <a b c d>;
say (#a ⊖ #a[0,1]).keys; # (c d)
It's basically the same solution you proposed at the beginning, using set difference, except it's using it on the whole array instead of on the indices. Also, in some cases you might use the set directly; it depends on what you want to do.
#raiphs solution combined with #Jonathan Worthington 's:
The operator should be very efficient for huge numbers and large #not-ats lists as it returns a list of ranges, and it even creates that list of ranges lazily. For the #not-ats it supports integers and ranges with included and excluded bounds and infinity. But it has to be ascending.
The $elems can be a Range or an Int. It is interpreted as .Int as in Jonathan Worthington's solution to support (but needs a .flat applying it to array slicing - the price of performance for the lazy operator - this could be changed by using flat gather instead of lazy gather in the 2nd line)
#a[ (* not-at (1, 3, 5)).flat ];
or newly support
#a[ (* not-at (1, 3^ .. 5, 8 .. 8, 10, 14 .. ^18, 19 .. *)).flat ];
The performance improvements can be seen, when not slicing the array at once, but operating on parts of the array, optimally with multithreading.
sub infix:<not-at> ($elems, #not-ats) {
lazy gather {
my $at = 0;
for #not-ats { # iterate over #not-ats ranges
my ($stop, $continue) = do given $_ {
when Int { succeed $_, $_ } # 5
when !.infinite { succeed .int-bounds } # 3..8 | 2^..8 | 3..^9 | 2^..^9
when !.excludes-min { succeed .min, $elems.Int } # 4..*
default { succeed .min + 1, $elems.Int } # 3^..*
}
take $at .. $stop - 1 if $at < $stop; # output Range before current $not-at range
$at = $continue + 1; # continue after current $not-at range
}
take $at .. $elems.Int - 1 if $at < $elems; # output Range with remaining elements
}
}
I'm not a programmer but find myself writing some simple Ruby. I need to populate an array with a list of IP addresses, 10.13.203.3, 10.13.204.3, etc.
I know that I can expand a range into an array like this, but how could I turn that into my IP address pattern?
a =* (3..10)
#=> [3, 4, 5, 6, 7, 8, 9, 10]
I was thinking I would need to do string interpolation and somehow feed it back into a different array. But I'm really not sure and mustn't be Googling correctly because this feels like a common thing that would be done.
You can use the IPAddr class, specifically the method IPAddr::new.
I have written the method to take three arguments, the first IP, the next IP and the total number of IPs to generate while maintaining the difference between the first and second IPs.
require 'ipaddr'
def gen_ips(ip_start, ip_next, number)
ips, ipn = [ip_start, ip_next].map { |ip| IPAddr.new(ip) }
(ips..IPAddr.new("255.255.255.255")).
step(ipn.to_i-ips.to_i).
lazy.
map { |ip| ip.to_s }.first(number)
end
gen_ips("10.13.203.3", "10.13.204.3", 6)
# => ["10.13.203.3", "10.13.204.3", "10.13.205.3", "10.13.206.3",
# "10.13.207.3", "10.13.208.3"]
gen_ips("10.13.254.250", "10.13.254.252", 6)
#=> ["10.13.254.250", "10.13.254.252", "10.13.254.254", "10.13.255.0",
# "10.13.255.2", "10.13.255.4"]
The second example shows what happens when the next IP rolls a digit in a preceding group.
I used Enumerable#lazy to convert the enumerator to a lazy one, so first(number) would be invoked as soon as number of elements of the mapping had been computed (rather than waiting until the enumerator had generated the last one in the range, IPAddr('255.255.255.255')).
You're looking for map
Ranges are enumerables so you can call map on them. And it's also simpler and easier to understand:
(203..210).map { |i| "10.13.#{i}.3" }
Would give you:
#=> ["10.13.203.3", "10.13.204.3", "10.13.205.3", "10.13.206.3", "10.13.207.3", "10.13.208.3", "10.13.209.3", "10.13.210.3"]
(203..210).inject([]) { |ar, i| ar << "10.13.#{i}.3" }
# [
# "10.13.203.3",
# "10.13.204.3",
# "10.13.205.3",
# "10.13.206.3",
# "10.13.207.3",
# "10.13.208.3",
# "10.13.209.3"
# "10.13.210.3"
# ]
References:
Enumerable#inject
I found this example of using #any? on a hash a bit tricky:
"With a Hash, you can use these in two ways. Either with one argument that is a 2 element array of the key-value pair. candidate[0] is the key and candidate[1] is its value.
{:locke => 4, :hugo => 8}.any? { |candidate| candidate[1] > 4 }
This returns true because the value of the second candidate :hugo is greater than 4."
Could anyone point me someplace that explains what happened here? I couldn't find a relevant question on SO. Thanks a lot in advance.
If you print candidate it will become easy to understand:
{:locke => 4, :hugo => 8}.any? { |candidate| puts candidate.to_s }
# [:locke, 4]
# [:hugo, 8]
The any? method is treating each key-value pair of the hash as an a two-element array, which means the hash will be treated as an array of arrays.
The block passed to any? (i.e., { |candidate| candidate[1] > 4 }) returns true if any of the the second elements (i.e., 4 and 8) is ever > 4 and false otherwise. 8 > 4, so the result is true.
From the official docs, the any? method:
Passes each element of the collection to the given block. The method
returns true if the block ever returns a value other than false or
nil. If the block is not given, Ruby adds an implicit block of { |obj|
obj } that will cause any? to return true if at least one of the
collection members is not false or nil.
What Hash#any? does is yield arrays of two elements to a given block and return true if the evaluation of the block returned something truthy (not false or nil) and false otherwise.
As for why you get two values if you pass a block with two arguments - this is called unpacking.
def foo
yield [42, 6 * 9]
end
# only one argument, array is not unpacked
foo { |numbers| p numbers } # prints [42, 54]
# two arguments, the array is being unpacked
foo { |x, y| p x, y } # prints 42 \n 54
On this page, it shows how to initialize an array, and if you scroll down a bit, under the section called "The Lists" it "explains" what lists are and how they're different from arrays.
Except it uses an example that's just exactly the same as declaring an array, and doesn't explain it whatsoever.
What is the difference?
Take a look at perldoc -q "list and an array". The biggest difference is that an array is a variable, but all of Perl's data types (scalar, array and hash) can provide a list, which is simply an ordered set of scalars.
Consider this code
use strict;
use warnings;
my $scalar = 'text';
my #array = (1, 2, 3);
my %hash = (key1 => 'val1', key2 => 'val2');
test();
test($scalar);
test(#array);
test(%hash);
sub test { printf "( %s )\n", join ', ', #_ }
which outputs this
( )
( text )
( 1, 2, 3 )
( key2, val2, key1, val1 )
A Perl subroutine takes a list as its parameters. In the first case the list is empty; in the second it has a single element ( $scalar); in the third the list is the same size as #array and contains ( $array[0], $array[1], $array[2], ...), and in the last it is twice as bug as the number of elements in %hash, and contains ( 'key1', $hash{key1}, 'key2', $hash{key2}, ...).
Clearly that list can be provided in several ways, including a mix of scalar variables, scalar constants, and the result of subroutine calls, such as
test($scalar, $array[1], $hash{key2}, 99, {aa => 1, bb => 2}, \*STDOUT, test2())
and I hope it is clear that such a list is very different from an array.
Would it help to think of arrays as list variables? There is rarely a problem distinguishing between scalar literals and scalar variables. For instance:
my $str = 'string';
my $num = 99;
it is clear that 'string' and 99 are literals while $str and $num are variables. And the distinction is the same here:
my #numbers = (1, 2, 3, 4);
my #strings = qw/ aa bb cc dd /;
where (1, 2, 3, 4) and qw/ aa bb cc dd / are list literals, while #numbers and #strings are variables.
Actually, this question is quite well answered in Perl's FAQ. Lists are (one of) methods to organize the data in the Perl source code. Arrays are one type of storing data; hashes are another.
The difference is quite obvious here:
my #arr = (4, 3, 2, 1);
my $arr_count = #arr;
my $list_count = (4, 3, 2, 1);
print $arr_count, "\n"; # 4
print $list_count; # 1
At first sight, there are two identical lists here. Note, though, that only the one that is assigned to #arr variable is correctly counted by scalar assignment. The $list_count stores 1 - the result of evaluating expression with comma operator (which basically gives us the last expression in that enumeration - 1).
Note that there's a slight (but very important) difference between list operators/functions and array ones: the former are kind-of omnivores, as they don't change the source data, the latter are not. For example, you can safely slice and join your list, like this:
print join ':', (4,2,3,1)[1,2];
... but trying to 'pop' it will give you quite a telling message:
pop (4, 3, 2, 1);
### Type of arg 1 to pop must be array (not list)...
An array is a type of variable. It contains 0 or more scalars at monotonously increasing indexes. For example, the following creates array #a:
my #a;
Being variables, you can manipulate arrays. You can add elements, change the values of elements, etc.
"List" means many things. The two primary uses for it are to refer to list values and instances of the list operator.
A list value is an ordered collection of zero or more scalars on the stack. For example, the sub in the following code returns a list to be assigned to #a (an array).
my #a = f();
List values can't be manipulated; they are absorbed in whole by any operator to which they are passed. They are just how values are passed between subs and operators.
The list operator (,) is an N-ary operator* that evaluates each of its operands in turn. In list context, the list operator returns a list consisting of an amalgamation of the lists returned by its operands. For example, the list operator in the following snippet returns a list value consisting of all the elements of arrays #a and #b:
my #c = ( #a, #b );
(By the way, parens don't create lists. They're just there to override precedence.)
You cannot manipulate a list operator since it's code.
* — The docs say it's a binary operator (at least in scalar context), but it's not true.
Simple demonstration of difference.
sub getarray{ my #x = (2,4,6); return #x; }
sub getlist { return (2,4,6); }
Now, if you do something like this:
my #a = getarray();
my #b = getlist();
Then #a and #b will both contain the same value - the list (2,4,6). However, if you do this:
my $a = getarray();
my $b = getlist();
Then $a will contain the value 3, while $b will contain the value 6.
So yes, you can say that arrays are variables containing list values, but that doesn't tell the whole story, because arrays and literal lists behave quite differently at times.
Lists are comma-separated values (csv) or expressions (cse) . Arrays (and hashes) are containers.
One can initialize an array or hash by a list:
#a = ("profession", "driver", "salary", "2000");
%h = ("profession", "driver", "salary", "2000");
One can return a list:
sub f {
return "moscow", "tel-aviv", "madrid";
}
($t1, $t2, $t3) = f();
print "$t1 $t2 $t3\n";
($t1, $t2, $t3) is a list of scalar containers $t1, $t2, $t3.
Lists are a form of writing perl expressions (part of syntax) while arrays are data structures (memory locations).
The difference between lists and arrays confuses many. Perl itself got it wrong by misnaming its builtin function wantarray(): "This function should have been named wantlist() instead." There is an answer in perlfaq4, "What is the difference between a list and an array?", but it did not end my confusion.
I now believe these to be true:
An array in scalar context becomes a count of its elements.
The comma operator in scalar context returns the last element.
You can't make a reference to a list; \(2, 4, 6) returns a list of references to the scalars in the list. You can use [2, 4, 6] to make a reference to an anonymous array.
You can index a list (to get its nth element) without making an array if you make a list slice, so (2, 4, 6)[1] is 4.
But what if I want to count the elements in a list, or get the last element of an array? Should I convert between arrays and lists somehow?
You can always convert a list to an array with [...] syntax. One way to count the elements in a list is to make an anonymous array, then immediately dereference it in scalar context, like so:
sub list { return qw(carrot stick); }
my $count = #{[list()]};
print "Count: $count\n"; # Count: 2
Another way is to use the list assignment operator, like so:
sub list { return qw(carrot stick); }
my $count = (()=list());
print "Count: $count\n"; # Count: 2
There is no array in this code, but the list assignment operator returns the number of things being assigned. I assign them to an empty list of variables. In code golf, I write ()=$str=~/reg/g to count the regular expression matches in some string.
You need not convert an array to a list, because an array in list context is already a list. If you want the last element of an array, just say $array[-1].
The comma operator would return the last element of a list, but I can't use it to get the last element of an array. If I say ((),#array) in scalar context, then #array is in scalar context and I get the count.
You need not make an array to index a list. You can make an anonymous array, as in [list()]->[1], or you can make a list slice, as in (list())[1]. I had trouble with list slices because they have different syntax. A list slice needs parentheses! In C or Python or Ruby, func()[1] would do the array index on the function's return value, but in Perl, func()[1] is a syntax error. You must say (func())[1].
For example, I want to print the 3rd highest number in array. Because I'm lazy, I sort the array and take the 3rd last element:
my #array = (112, 101, 114, 108, 32, 104, 97, 99, 107);
print +(sort { $a <=> $b } #array)[-3], "\n"; # prints 108
The unary + prevents the print() function stealing my parentheses.
You can use a list slice on an array, as in (#array)[1]. This works because an array is a list. The difference between lists and arrays is that arrays can do $array[1].
An Array Vs A List
A list is a different kind of data structure from an array.
The biggest difference is in the idea of direct access Vs sequential access. Arrays allow both; direct and sequential access, while lists allow only sequential access. And this is because the way that these data structures are stored in memory.
In addition, the structure of the list doesn’t support numeric index like an array is. And, the elements don’t need to be allocated next to each other in the memory like an array is.
Arrays
An array is an ordered collection of items, where each item inside the array has an index.
here my answer about sigils and context
but main difference is this:
arrays have a scalar-context-value like count of elements.
lists have a scalar-context-value like LAST element in list.
so, you need to know about goat-operator: =()=.
Usage?
perl -e '$s =()= qw(a b c); print $s' # uh? 3? (3 elements, array context)
perl -e '$s = qw(a b cLastElementThatYouSee); print $s' # uh? cLastElementThatYouSee? (list context, last element returned)
as you see, =()= change context to array