Re: Introducing loopfusion: loop over any number of sequences of any single type.
Yes, I actually started with a macro that produces a variadic zip. It's still untouched in my code. The reason is that the following does not compile, error is `Error: iterator within for loop context expected` template zipTest(arguments: varargs[untyped]): untyped = iterator zipZipZip(a: seq[int], b: seq[bool], c: seq[int], d: seq[float]): (int, bool, int, float) = let size0 = a.len for i in 0..
Re: Introducing loopfusion: loop over any number of sequences of any single type.
Nice, but is there a reason this couldn't have been implemented as an iterator?
Re: Introducing loopfusion: loop over any number of sequences of any single type.
I've updated the package, it now supports in-place mutation of the inputs and sequence of different subtypes as input. Here is a snippet of the syntax. import loopfusion block: # Simple let a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] forEach x in a, y in b, z in c: echo (x + y) * z # 120 # 140 # 160 block: # With index let a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] var d: seq[int] = @[] forEach i, x in a, y in b, z in c: d.add i + x + y + z doAssert d == @[22, 25, 28] block: # With mutation var a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] forEach x in var a, y in b, z in c: x += y * z doAssert a == @[111, 122, 133] block: # With mutation, index and multiple statements var a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] forEach i, x in var a, y in b, z in c: let tmp = i * (y - z) x += tmp doAssert a == @[1, 4, 9] block: # With iteration on seq of different types let a = @[1, 2, 3] let b = @[false, true, true] forEach integer in a, boolean in b: if boolean: echo integer
Re: Introducing loopfusion: loop over any number of sequences of any single type.
Done import loopfusion let a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] forEach x in a, y in b, z in c: echo (x + y) * z # 120 # 140 # 160 # i is the iteration index [0, 1, 2] forEach i, x in a, y in b, z in c: d.add (x + y) * z * i # index: 0, 120 # index: 1, 140 # index: 2, 160 The tricky thing was the difference between typed and untyped macro. I can't do all in the same one. I have removed the ForEachIndexed and added it to forEach. It was a workaround because somehow I use overloading previously.
Re: Introducing loopfusion: loop over any number of sequences of any single type.
> Syntactically I would prefer something that aligns the loop variables with > the containers Yes, this looks nice. Btw, a similar syntax exists in Julia, but what it does is completely different - it is syntactic sugar for nested for-loops. > Indeed that's a much better syntax, I have absolutely no idea how to > introduce it though. For me, a step forward to a better syntax would be just introducing in instead of , between elements and containers: forEach [x, y, z] in [a, b, c]: Also, changing square brackets to parentheses looks more intuitive to me, as I expect zip to yield tuples: forEach (x, y, z) in (a, b, c):
Re: Introducing loopfusion: loop over any number of sequences of any single type.
1\. For your first question, it shouldn't introduce overhead as it's an inline iterator. You can check the generated C code. If we take a simple seq this is equivalent to the following iterator foo_item[T](s: seq[T]): T = for i in 0 ..< s.len: yield s[i] let a = @[1, 2, 3, 4] for val in foo_item(a): echo val The reason why I use an intermediate zip is for Arraymancer. This is a proof of concept before I generalize it to tensors. On tensors, it would be wrapped in an OpenMP template that splits the work on multiple cores like [here](https://github.com/mratsim/Arraymancer/blob/e801b78169b59f1213a2e046997cfecf99ed4feb/src/tensor/higher_order_applymap.nim#L31-L43). I also really need an iterator because the "next" item is not straightforward if the tensor is not contiguous, see [here](https://github.com/mratsim/Arraymancer/blob/e801b78169b59f1213a2e046997cfecf99ed4feb/src/tensor/private/p_accessors.nim#L144-L148). 2\. Thanks, fixed. Yes if they are not the same length it generates an error. 3\. Indeed that's a much better syntax, I have absolutely no idea how to introduce it though. Bonus, I added loopFusion: import loopfusion let a = @[1, 2, 3] let b = @[11, 12, 13] let c = @[10, 10, 10] let d = @[5, 6, 7] loopFusion(d,a,b,c): let z = b + c echo d + a * z
Re: Introducing loopfusion: loop over any number of sequences of any single type.
Looks interesting, but why does the macro generate a for loop plus a zip iterator (which itself contains another for loop) instead of just a single for loop with injected symbols? forEachIndexed j, [x, y, z], [a, b, c]: body # would expand to: for j in 0 ..< commonLengthWithSanityCheck(a, b, c): let x = a[j] let y = b[j] let z = c[j] body Note: The length of `b` in your example differs. I was wondering if this is not handled, but I think it is, so probably just a typo . Syntactically I would prefer something that aligns the loop variables with the containers, i.e., forEachIndexed j, x in xs, y in ys, z in zs: ...
Introducing loopfusion: loop over any number of sequences of any single type.
After diving deep in the wonderful world of metaprogramming today, I'm happy to share the [loopfusion](https://github.com/numforge/loopfusion) package. For now you need to install it through `nimble install https://github.com/numforge/loopfusion`. This will give you 2 macros forEach and forEachIndexed that will allow you to iterate and operate over any number of sequences of any same type. Examples: import loopfusion let a = @[1, 2, 3] let b = @[11, 12, 13, 10] let c = @[10, 10, 10] forEach [x, y, z], [a, b, c]: echo (x + y) * z forEachIndexed j, [x, y, z], [a, b, c]: echo "index: " & $j & ", " & $((x + y) * z) 120 140 160 index: 0, 120 index: 1, 140 index: 2, 160 import loopfusion let a = @[false, true, false, true, false] let b = @[1, 2, 3, 4, 5] let c = @["a: ", "b: ", "c: ", "d: ", "e: "] var d: seq[int] = @[] forEachIndexed j, [x, y, z], [a, b, c]: if x: d.add $(y*y) else: d.add $y echo d @[0, 140, 320]