Re: Thinking Aloud: Refactoring from nested loops to enumerators and lambda's

Robert Klemme <[email protected]> Tue, 2 Jul 2019 18:46:58 +0200
Newsgroups gmane.comp.lang.ruby.general
Message-ID <CAM9pMnNF+QXWm9Jek2dHenB6-NNqOszTKjRPZEMi00Z0oKJFEA@mail.gmail.com>
On Mon, Jul 1, 2019 at 6:13 AM John Carter <[email protected]>
wrote:

> So I'm just thinking out aloud here.
>
> Being an Old School Programmer I tend to naturally to write ever more
> deeply nested loops.
>
> I hate myself when I do this because it’s hard to test especially if
> some of the loops have nasty external side effects, it’s hard to
> reuse, it’s hard to refactor.
>
> So I’m trying two new patterns....
>
> * Passing enumerators as parameters so I can pull out inner loops as
> standalone functions without calling the entire function every time..
>
> * Creating functions that return lambdas, so I can pass an inner loop
> in as a parameter.
>
> Walk with me this is going to be long…. the example is a teaching
> example / dojo exercise for myself, so excuse me it it slightly
> contrived.
>
> (In the following, a line of ===== indicates the next, slightly
> different version of the code)
>
> ==========================================
>
> Here is a typical chunk of my code…
>
> def nested( a, b, c)
>
>    stuff_a = func_a(a)
>    stuff_b = func_b(b)
>    stuff_c = func_c(c)
>    result = {}
>    func_1( stuff_a) do |a1|
>       stuff_d = func_d( a1 + stuff_b)
>       func_2( stuff_d) do |a2|
>          stuff_e = func_e( a2 + stuff_c)
>
>          func_3( stuff_e) do |a3|
>
>             result[a3] = func_f( a3)
>          end
>       end
>    end
>
>    result
> end
>

I mulled a bit about this and here are a few unsorted and incomplete
thoughts:
* The pattern implies that func_1, func_2 and func_3 do an iteration
internally. Why not extract the Enumerable directly and use that to make
the iteration more visible? Alternatively, if these function filter you
could write a getter that returns an Enumerator.
* Why not just put the logic of the iteration and body in classes of
stuff_a, stuff_d and stuff_e?
* func_d and func_e might be better off having two arguments and doing
addition internally.
* I think I really agree to Brandon: without a more real example and
without knowledge what all the methods actually do it is extremely
difficult to come up with good suggestions.


> It’s fairly clear but has a few gotchas.
>
> * It’s unclear which part of the code actually depends on which parameters.
> * In this toy example, the function is small… but a Real Life nested
> loop function like this can quickly grow hideously large.
>

But that is an issue in itself that points to issues with the design of the
data structures or logic.


> * As a “premature optimization” I have factored out subexpressions
> that do not alter within the loops, resulting in large scopes for
> variables that are only used inside the loops.
>

The method in this example is pretty short so I do not see an issue with
that. In other cases that problem automatically goes away if you distribute
the logic properly across multiple methods / functions.


> * func_1(), func_2(), func_3() yield a stream of things….but a stream
> of things should just be an enumerable!
>

Exactly, see above.

Kind regards

robert


Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>