rastoboy has asked for the wisdom of the Perl Monks concerning the following question:

Is there a cleaner way to do this:
foreach my $eth (@eths) { foreach my $net (@nets) { $netifaces->{$eth}{$net} = 1; } }
edit: forgot the arrow, fixed--apologies

Replies are listed 'Best First'.
Re: I hate nested loops
by davido (Cardinal) on Aug 10, 2011 at 20:50 UTC

    It's hard to beat nested foreach loops for simplicity and readability in the example you provided. But if there is a Perl5 alternative, it's probably found in tye's Algorithm::Loops. In specific, you could accomplish the same like this:

    NestedLoops( [ \@eths, \@nets ], sub{ $netfaces->{ $_[0] }{ $_[1] } = 1; } );

    Your posted example is what everyone's going to expect to see. But I would guess that any reasonably experienced programmer would be able to look at the snippet posted in this node and know pretty quickly what is going on.


    Dave

      Awesome, y'all, thanks!

      I don't want to obfuscate it or anything, I really just had an (apprently wrong) intuition that there ought to be a nice, non-obfuscated way of making it more elegant.

      I used to have a nested loop problem, which Granfather here on PM talked me out of, and I've been so much happier, since :-)

Re: I hate nested loops
by moritz (Cardinal) on Aug 10, 2011 at 20:18 UTC
Re: I hate nested loops
by BrowserUk (Patriarch) on Aug 10, 2011 at 19:55 UTC

    Still nested loops, just the inner one is hidden:

    @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths;

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      The double foreach loop is a whole lot easier to read though ;-)

      Jason L. Froebe

      Blog, Tech Blog

        The double foreach loop is a whole lot easier to read though ;-)

        I don't know. Maybe for a newbie, but I don't find it so. I have no trouble reading slice syntax -- I use it a lot -- and find the way it extends from 1D to 2D and so on, very natural and readable.

        More importantly, I prefer the single line syntax to the multi-line because it makes it very clear that this is simply initialising the HoHs to a bunch of 1s, and nothing more. I like that I can read as "Initialise the 2D hash to 1s". There is little opportunity to misread the intent and screw it up by injecting extra stuff in the loops.

        It would be even nicer if I could do (something like):

        my %netifaces{ @eths }{ @nets } = (1) x *;

        My point I guess is that this initialisation (whilst possibly important), is essentially a trival part of the overall algorithm, and having it as a single line gives it a weight commensurate with that.

        Conversely, spreading it out over 5 lines with named iterators makes it take on a weight that makes it seem far more significant than it is in the overall scheme of things.

        Ie. This is but a single step in the algorithm, and having it as a single line in the source code makes that very clear. Which I like, and feel is helpful when reading the overall algorithm.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
      That looks interesting, but I do not understand it. I don't suppose you'd care to insert some parentheses, or explain it?
        I don't suppose you'd care to insert some parentheses,

        I don't think adding unnecessary parens would help at all :)

        or explain it?

        Sure:

        @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths; ^^^^^^^^^^ a hash reference @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths; ^^^^^^^^^^^^^^^^^^ an element in that hash @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths; iterated over ^^^ + @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths; by these keys ^^^^^ @{ ... }{ @nets } ^^^^^^^^^^^^^^^^^^ a hash slice with the keys in @nets @{ ... }{ @nets } = (1) x @nets ...; is assigned ^ ^^^^^^^^^^^ a list of '1's the size of @nets

        Putting that all together, we get:

        For each value in @eths, create a key in $netifaces with a value that consists of a hash containing all of the keys in @nets, each with the value 1.

        Eg.

        @{ $netifaces->{ $_ } }{ @nets } = (1) x @nets for @eths;

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: I hate nested loops
by davido (Cardinal) on Aug 11, 2011 at 18:15 UTC

    To me, for idiomatic clarity I find the original nesting of for/foreach loops to be superior. The Algorithm::Loops method was initially less familiar to me, but once I put it to use it became very legible, and I imagine it would be even to someone who hadn't read the Algorithm::Loops documentation.

    The slice method is a little less legible to me. I use slices all the time, but there's just something about how it plays out that makes it less than obvious at first glance what it going on. Reading through it a little more carefully, it's easy enough to understand, but it's not a segment of code that, to me at least, screams "This is what I do." However, it has the advantage of being the most computationally efficient solution, which I'll demonstrate in a moment.

    For good measure I played with a nested map option too. I find it fairly easy to read; seeing the keyword map twice, and one buried inside of brackets tells me they're nested.

    I was kind of hesitant to post this as it might motivate people jump to computational efficiency as a means of driving coding decisions, when maintainability and clarity probably ought to take the driver's seat unless there is a known issue. So look at the following from the perspective of already having determined that you've got a bottleneck.

    That produced the following results:

    ok 1 - Control matched. ok 2 - Control matched. ok 3 - Control matched. Rate NestedLoops mapped foreach sliced NestedLoops 5.17/s -- -89% -91% -95% mapped 49.0/s 848% -- -19% -49% foreach 60.2/s 1065% 23% -- -37% sliced 96.2/s 1760% 96% 60% -- 1..3

    Dave

      I was kind of hesitant to post this as it might motivate people jump to computational efficiency as a means of driving coding decisions,

      Once you put the code into a subroutine, all you need to do is give it a good name, say init2Dhash() or whatever makes sense in the context of the code, and what idiom is used inside the sub becomes irrelevant.

      At that point, you might as well go for the most efficient solution.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.