Array stays array when looped through `for` - raku

I'm looping through the depends array from a META6.json. I've loaded into a Hash using JSON::Fast. When I'm looping through it using a for loop, however, it only goes through the loop once, and the item is the same array:
use JSON::Fast;
my %meta = from-json(slurp("META6.json"));
for %meta<depends> -> $dependency {
dd $dependency;
}
This piece of code returns
Array $dependency = $["Config::Parser::toml:ver<1.0.1+>", "Config:api<1>:ver<1.3.5+>", "Dist::Helper:ver<0.21.0+>", "Hash::Merge", "Terminal::Getpass:ver<0.0.5+>", "zef"]
I'm expecting it to loop through the %meta<depends> 6 times, which each iteration holding a different element from that array.
For good measure, this is the output of dd %meta<depends> from the example:
Array %meta = $["Config::Parser::toml:ver<1.0.1+>", "Config:api<1>:ver<1.3.5+>", "Dist::Helper:ver<0.21.0+>", "Hash::Merge", "Terminal::Getpass:ver<0.0.5+>", "zef"]
Why is the loop not looping the way I expected?
EDIT: I'm using the latest Rakudo Star:
This is Rakudo Star version 2018.04.1 built on MoarVM version 2018.04.1
implementing Perl 6.c.

Even though %meta<depends> contains an Array, it is contained inside an item (container). The for statement looks at that and decides there's only 1 thing to iterate over (the container).
This is easily remedied: by suffixing .list you convert the item to something Iterable, and thus it will iterate over the Array in the container:
for %meta<depends>.list -> $dependency {
A slightly shorter syntax for this is #():
for #(%meta<depends>) -> $dependency {
EDIT: or use the syntax suggested by jjmerelo, which decontainerizes the element, and thus exposes the underlying Array to for:
for %meta<depends><> -> $dependency {

This is a pitfall. Essentially this is like:
my $var = ['a', 'b', 'c'];
for $var -> $v {
dd $v;
}
Which gives you: $["a", "b", "c"]
If you iterate an array with # sigil it already acts as an Array, but when you have a list inside a scalar it will return the Array and not iterate inside it.
The solution is to use .list to make it act as a list instead of as a scalar.

Related

Why is it that sometimes we have to use coercion to access individual elements of an array?

My understanding is that in the code below I wouldn't have to use (2), just (1) would suffice to print each element of the array in its own line.
$ cat test-hash.raku
use v6;
my %h;
say "";
say "inserts a scalar";
%h.push: (aaa => "aaa");
%h.say;
say "";
say "turns the scalar into an array";
%h.push: (aaa => "AAA");
%h.say;
say "";
say "(1) shows all elements of the array in a line";
.say for %h{"aaa"};
say "";
say "(2) shows each element of the array in a line";
.say for %h{"aaa"}.Array;
$ raku test-hash.raku
inserts a scalar
{aaa => aaa}
turns the scalar into an array
{aaa => [aaa AAA]}
(1) shows all elements of the array in a line
[aaa AAA]
(2) shows each element of the array in a line
aaa
AAA
$
You could argue that this is a bug in the implementation of Array.push, because it assigns an Array to the Hash element, rather than binding to it. Because it assigns, the Array lives in a Scalar container, and is effectively itemized. And being itemized, it won't be iterated over.
So, the easiest solution is to de-itemize the Hash elements before iterating:
.say for %h{"aaa"}<>;
See decontainerization operator for more info.

Flatten array of arrays to one array in Kotlin

I have the following Java 11 code (the contents of arr1 and arr2 are not so simple in my code, and I have more than 2 arrays, but the concept is the same):
String[] arr1 = new String[] {"a","b"};
String[] arr2 = new String[] {"c", "d"};
var req = Stream.of(arr1, arr2).flatMap(Stream::of).toArray(String[]::new);
The purpose of this code is to take all the values in multiple arrays of Strings and produce a single String array out. It needs to be an array, not a collection, due to an API outside my control accepting a String array later in the code.
In this simple example, the resulting array should have the following elements in this order: { "a", "b", "c", "d" }.
What is the canonical way to flatten a 1-deep array of arrays into a single array in Kotlin?
The main reason I'm being thrown for a loop here is that the IntelliJ Java to Kotlin converter did a pretty bad job of converting this code, leaving it with multiple weird syntax errors in the output Kotlin. The rest of my code that doesn't use things like method references converted much more cleanly to Kotlin.
I don't know about canonical, but perhaps the simplest equivalent is:
val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val req = arrayOf(arr1, arr2).flatten().toTypedArray()
That creates an Array<String> with the four values you want.
Here we're not transforming the values, simply repackaging them, so flatten() is simpler than the more common flatMap.
(It's normally better to use lists and other collections — the standard library has much better support for them, as well as avoiding issues around generics and type erasure — but having to interoperate with an old or badly-designed API, as specified in this question, is one of the corner cases you may still need arrays for, along with varargs and low-level collection implementation.)
The easiest in Kotlin would be:
val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val result = arrayOf(arr1, arr2)
.flatten()
.toTypedArray()
flatten() creates a List and toTypedArray() converts it to an array. This could be considered a waste of CPU cycles, but on the other hand, I don't see a way to create an array from a lazy stream/sequence directly, because we don't know the resulting size. So my guess would be that Java's Stream.toArray() also copies the data several times in the process (?).
If we need to limit copying to the minimum, we can create our own extension:
inline fun <reified T> Array<out Array<out T>>.flattenToArray(): Array<T> {
val result = arrayOfNulls<T>(sumOf { it.size })
var pos = 0
for (arr in this) {
arr.copyInto(result, pos)
pos += arr.size
}
#Suppress("UNCHECKED_CAST")
return result as Array<T>
}
If you literally just want to put them in a single array, you can use the spread operator
val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val result = arrayOf(*arr1, *arr2)
I'm not sure there's a way to do that for an arbitrary number of source arrays, so if you need that, then flatten is the way to go. This is quick and easy for unpacking a specific bunch of arrays though
.flatten() should do the job.
val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
arrayOf(arr1, arr2).flatten()
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/flatten.html
flatten() is the simple way to do it. All the standard array and Iterable operators return Lists. If you actually need an array, you can call toTypedArray() on the returned List.
You can start by combining them into a List or Array.
val arr1 = arrayOf(“a”, “b”)
val arr2 = arrayOf(“c”, “d”)
val req = listOf(arr1, arr2).flatten() //.toTypedArray()

Getting "value without a container" error

Got this:
for $config.IO.slurp.lines <-> $l {
$l .= trim;
...
}
Get this:
t/01-basic.rakutest ..3/5
Parameter '$l' expects a writable container (variable) as an argument,
but got '# karabiner config file' (Str) as a value without a container.
in sub generate_file at...
I've read the docs on containers but it didn't shed any light on what I can do in this situation aside from maybe assigning $l to a scalar variable, which seems hacky. Is there a way I can containerize $l?
The issue is really that .lines does not produce containers. So with <->, you would bind to the value, rather than a container. There are several ways to solve this, by containerizing as you suggested:
for $config.IO.slurp.lines -> $l is copy {
$l .= trim;
...
}
But that only makes sense if you want to do more changes to $l. If this is really just about trimming the line that you receive, you could do this on the fly:
for $config.IO.slurp.lines>>.trim -> $l {
...
}
Or, if you need to do more pre-processing $l, use a .map:
for $config.IO.slurp.lines.map({
.trim.subst("foo","bar",:g)
}) -> $l {
...
}
Maybe below is what you want? Generally, you read a file via slurp you can comfortably handle its size, or you read a file via lines if you want input taken in lazily, one-line-at-a-time:
my $config = 'alphabet_one_letter_per_line.txt';
my $txt1 = $config.IO.slurp;
$txt1.elems.say; #1
$txt1.print; #returns alphabet same as input
my $txt2 = $config.IO.lines;
$txt2.elems.say; #26
$txt2.join("\n").put; #returns alphabet same as input
Above, you get only 1 element when slurping, but 26 elements when reading lines. As you can see from the above code, there's no need to "...(assign) $l to a scalar variable..." because there's no need to create (temporary variable) $l.
You can store text in #txt arrays, and get the same number of elements as above. And you can just call routines on your stored text, as you have been doing (example below continues $txt2 example above):
$txt2.=map(*.uc);
say $txt2;
Sample Output:
(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
[Note, this question seems to have triggered questions on the use of $txt2.=map(*.uc); versus $txt2.=uc;. My rule-of-thumb is simple: if the data structure I'm working on has more than one element, I map using * 'whatever-star' to address the routine call to each element].
https://docs.raku.org/

How to declare a function that accepts a typed array parameter

Say I want to declare a function whose parameter is an array of strings:
sub process-string-array(Str[] stringArray) # invalid
{
...
}
How would I do that ?
It depends on the sigil you want to use:
sub process-string-array(Str #array) { ... } # #-sigil
sub process-string-array(Array[Str] $array) { ... } # $-sigil
Note that you have to be careful to pass in a declared Str array to do this which means adhoc arrays will need to passed in with a typed declaration:
my Str #typed-array = <a b c>;
process-string-array <a b c>; # errors
process-string-array #typed-array; # typed array in
process-string-array Array[Str].new: <a b c>; # adhoc typed array
If you don't want to deal with typing arrays like this, you can use a where clause to accept any Any-typed array that happens to include only Str elements (which is often easier to use IME):
sub process-string-array(#array where .all ~~ Str) { ... }
This, however, (as jnthn reminds in the comments) requires type checking each element (so O(n) perf versus O(1) ), so depending on how performance sensitive things are, it may be worth the extra code noise. Per Brad's suggestion, you could multi it, to speed things up when the array is typed and fallback to the slower method when not.
multi sub process-string-array(Int #array) {
... # actual processing here
}
multi sub process-string-array(#array where .all ~~ Str) {
process-string-array Array[Int].new: #array
}

Kotlin - How to apply modulo operation on each element of a matrix?

I find Lambda in Kotlin to be very confusing and on the top of it is "it".
There are two things I know about "it" and i.e.
If your Lambda has their own argument, you can replace its name with "it".
"It" is an automatically generated name for your Lambda, if it has
only one argument, and you don't specify a different argument name.
Still I don't understand what actually passes as "it".
For E.g. I wanted to apply modulo function on each element of a 3x3 matrix.
fun main(){
var result = Array(3) {
IntArray(3) { 3;2;4;6;7;9;12;11;23 }
}
result = Array(3){ IntArray(3) {it%2} }
println(result.joinToString("\n") { it.joinToString(" ") })
}
Here I assumed that "it" takes each element of the matrix which is clearly not the case as my output was:
0 1 0
0 1 0
0 1 0
So can you please explain me how "it" works, what is happening here? and what would be the correct way to implement this program?
Your line
result = Array(3){ IntArray(3) {it%2} }
isn't doing anything to the original Array that result is pointing at. You are creating a brand new group of array objects by calling the Array and IntArray constructors.
The lambda that you pass to the IntArray constructor has an input parameter that represents the array index, and the return value of your lambda is what will be put into the array at that index. So in this case it is the array index, and your lambda is returning 0 and 1 for even and odd indices respectively.
You are also instantiating your array incorrectly to begin with. Your lambda that you pass to that IntArray constructor is throwing away a bunch of pointless Int values and then returning 23 for each item. So you've created a 3x3 matrix that is completely filled with the number 23.
The correct syntax for creating an array with explicit values is to use arrayOf or intArrayOf.
val result = arrayOf(
intArrayOf(3, 2, 4),
intArrayOf(6, 7, 9),
intArrayOf(12, 11, 23)
)
To modify all the values of an array, you typically iterate the traditional way, not with a lambda:
for (innerArray in result) {
for (i in innerArray.indices)
innerArray[i] = innerArray[i] % 2
}
You were probably thinking of the map function, which lets you pass a lambda and returns a new List with the lambda function applied to every element of the input collection. Or when working with collections other than arrays, you can use forEach or onEach to iterate them without modifying them.