Do we already have, or should we add functions for...
# stdlib
t
Do we already have, or should we add functions for multi dimensional for loops? For example:
Copy code
inline fun for4d(di: Iterable<Int>, ci: Iterable<Int>, bi: Iterable<Int>, ai: Iterable<Int>, block: (d:Int, c:Int, b: Int, a: Int) -> Unit) {
    for (d in di){for (c in ci){for (b in bi) {for (a in ai) {
            block(d, c, b, a)
    }}}}
}
Then we could do:
Copy code
fun main() {
    for4d(1..9, 0..9, 0..9, 0..9){i,j,k,l->
        //all 4 digit decimal numbers
        println("$i$j$k$l")
    }
}
Advantages: 1. Reduces the code indentation depth 2. Easier to type than multiple for loops; saving keystrokes for the round and curly bracket characters, "for"s and "in"s Limitations: 1. Only supports "for each element" use cases; does not support "for each dimension/row/column"
h
I don't think stdlib can really help you here. I think the way to achieve this is via the n-ary cartesian product. Rather than create a special type of loop, create a new iterable on which you can use a regular loop:
for(list in cartesianProduct(1..9, 0..9, 0..9, 0..9))
. Add destructuring and you can get
for((i, j, k, l) in cartesianProduct(1..9, 0..9, 0..9, 0..9))
See example here: https://pl.kotl.in/PM8HK5cCT Based on this stackoverflow answer which goes into a bit more detail (but focusses on sets rather than iterables)
👍 1
2
t
I see the beautiy in supporting looping multiple dimensions with a single function, but the n-ary cartesian product solution is horrendously inefficient. The "list of lists" wrapping is overkill for something so simple. I understand that there is no way around the type erasure issue if we have to support n-ary, but practically its just better to just create a function for each dimension variant instead.
Copy code
inline fun <A, B> loop2d(bi: Iterable<B>, ai: Iterable<A>, block: (b: B, a: A) -> Unit) {
    for (b in bi) for (a in ai) block(b, a)
}

inline fun <A, B, C> loop3d(ci: Iterable<C>, bi: Iterable<B>, ai: Iterable<A>, block: (c: C, b: B, a: A) -> Unit) {
    for (c in ci) for (b in bi) for (a in ai) block(c, b, a)
}

inline fun <A, B, C, D> loop4d(di: Iterable<D>, ci: Iterable<C>, bi: Iterable<B>, ai: Iterable<A>, block: (d: D, c: C, b: B, a: A) -> Unit) {
    for (d in di) for (c in ci) for (b in bi) for (a in ai) block(d, c, b, a)
}
h
No need to calculate and store all combinations upfront as in the example I gave. You can use a sequence instead, or define your own Iterator. That could help with efficiency in some cases, but if efficiency is your primary goal, then I don't think there's much that can beat your current solution. With different generic types I don't really see a nice solution that works with an arbitrary number of Iterables, so you'd be stuck with defining a function or Iterable/Sequence for each number of Iterables anyway.
You could consider dropping the 2d/3d/4d and just call them all
loop
. That makes it feel a bit more generic than it actually is when using it 🙂
t
Oh yeah that's true, that's how arrow does its functions too