crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Improving nested eachs (loops)

Open uninhm opened this issue 2 years ago • 3 comments

As you can see in this sample from the repo, it pretty common to need nested eachs, wouldn't it be nice to have a method this like showed below?

(0...9).nest((0...9)).nest((0...9)).each do |i, j, k|
  ...
end

Or maybe you come up with a better solution, the thing is, I'm sure we can improve this kind of nested loops.

uninhm avatar Jul 30 '22 19:07 uninhm

(0...9).to_a.each_cartesian((0...9).to_a, (0...9).to_a) do |(i, j, k)|
  # ...
end

HertzDevil avatar Jul 30 '22 19:07 HertzDevil

@HertzDevil Wouldn't that be way more memory consuming for larger ranges?

uninhm avatar Jul 30 '22 19:07 uninhm

If performance really does matter, you could use a macro:

macro each_many(*exprs, &block)
  {% for expr, i in exprs %}
    %iter{i} = {{ expr }}
  {% end %}
  {% for expr, i in exprs %}
    %iter{i}.each do |{{ block.args[i] }}|
  {% end %}
      {{ block.body }}
  {% for expr, i in exprs %}
    end
  {% end %}
end

each_many(0..2, 0..3, 0..4) { |i, j, k| p [i, j, k] }

This assumes #each never invalidates the receiver, so it doesn't work if e.g. one of the expressions is a Char::Reader or Dir.

I don't think such a macro would belong in the standard library since it provides little advantage over, well, nesting the loops manually (the number of expressions must be known at compile-time).

HertzDevil avatar Jul 30 '22 20:07 HertzDevil