in reply to Re: howto map/grep a complex structure
in thread howto map/grep a complex structure

I just want to point out that in this case, using prototypes is unnecessary (and actually not even used), and it's advised that you don't use them unless you know why you're using them. They're good for emulating and/or overriding built-in operators for instance.

In the following:

sub collectLeaves ($) { my ( $ds, $leaves ) = @_;

You've said "I take a single scalar", but then extract from @_ after calling the sub with two distinct params, but it works. Why? Because when you call your subs the old fashioned way with &, the prototype is ignored. If you remove the & from the calls, your @_ will only contain the very last scalar element of all the params you've passed in. In fact, you'd get an error in your case, because you're sending in two params when the prototype says it only accepts one:

sub this ($){ my @this = @_; print @this; } this(1, 1); __END__ Too many arguments for main::this at ./sub.pl line 10, near "1)" Execution of ./sub.pl aborted due to compilation errors.

Back to the calls with &. It is legacy code, and other than a few rare cases, is really only needed when dereferencing a code reference. In your case, it's actually doing more harm than good as it makes the code very confusing for future maintainers. I'd have a look at the code and remove the &, but then prototypes may break. I'd then have to hunt down and figure out whether the prototypes are required, and remove them one at a time if they aren't. Or, a newer Perl programmer may add a sub call without & and not even understand why errors are thrown on their call, but not the others.

Also, upon first glance at the sub definition, I may miss the $ prototype, but a my ($x, $y) = @_; will be abundantly clear. I would then be thinking why for the love of all things good is $y undef after calling the sub like func(@a);, where @a is a list with two elements. I'm passing in a list of two items, but $x is the last element of the list and $y is completely borked. Prototypes are a very uncommon thing, and unless you've dealt with them (most haven't), the errors and especially the silent modifications can be quite the treat to figure out.

Replies are listed 'Best First'.
Re^3: howto map/grep a complex structure
by AnomalousMonk (Archbishop) on Aug 27, 2015 at 14:03 UTC

    I can't say I agree with the overall approach of trippledubs here, but I think the use of prototypes therein can be said to have a constructive and well understood purpose.

    As written,  collectLeaves() is recursive, and within the function the calls such as
        &collectLeaves( $_, $leaves ) for ( @{$ds} );
    serve the purpose of suppressing prototype checking so the  $leaves reference can be passed in recursive calls.

    Outside the function, prototyping allows calls like
        my @wanted = grep { $_ == 7 } collectLeaves $hashref1, collectLeaves $hashref2;
    to be made, while
        ... collectLeaves($hashref1, $hashref2) ...
    will fail to compile.

    c:\@Work\Perl\monks>perl -wMstrict -le "sub S ($) { return $_[0] + print 0+@{$_[0]}, ' elements in ', 0+$_[0] +; } ;; print S [], S [42], 33; " 0 elements in 7450732 1 elements in 30219348 74507333021934933
    (But  S([],[42]) will not compile.)

    Again, I don't say I necessarily agree with the approach, but I cannot agree that prototyping has been sprinkled thoughtlessly onto the code as a kind of magical correctness dust, as we have often seen. OTOH, I agree with you that the response of future maintainers to this code (or recursive functions in general) is a valid concern.


    Give a man a fish:  <%-{-{-{-<

      Hi Anomalous! Thank you for discerning that it is not on accident! Why do you not necessarily agree with this approach? The nihilist in me says I shouldn't care yet the objectivist wants to improve..

        Anonymonk originally posted a for-loop approach to processing a data structure, then asked for ways to make it more "elegant" with map and grep. Several Monks opined that, for several reasons, the for-loop code was perfectly good as it was and needed no ornamentation. I strongly agree.

        Anon then replied "Ok, point taken, but I'd still appreciate some map/grep examples." The implication I took was that approaches other than map/grep would also be of interest. Several Monks responded with examples, including your recursive approach. I strongly approve these efforts; this is a teaching/learning site, after all.

        So we come down to two questions that are somewhat at cross-purposes: "What's the best code for this specific application?" and "What other sorts of algorithmic approaches might be used in similar situations?" The answer to the first question is "The code we started with." The answer to the second is "There are lots of approaches that might be used with profit, including the one posted by trippledubs here!" I hope the divergence of the two questions helps explain the half-hearted nature of my approval.


        Give a man a fish:  <%-{-{-{-<

Re^3: howto map/grep a complex structure
by trippledubs (Deacon) on Aug 27, 2015 at 20:16 UTC

    Sorry Guvna, did not know you are in charge of what is legacy / old fashioned. Please consider that I tried to make it look cool, clear, & concise when you call it without parenthesis outside the sub. It also adds some compile time checking against calling it in the wrong context.

    I'd have a look at the code and remove the &, but then prototypes may break. If I break it, it won't work.

    Prototypes are a very uncommon thing, and unless you've dealt with them How do you become proficient if you never use them a first time?

    I'm passing in a list of two items, but $x is the last element of the list and $y is completely borked. Can't pass in a list without intentionally circumventing the prototype.

    It could be re-written but then all the new perl programmers would be calling it in list context on accident and breaking everything.. those clumsy new guys. Here is another argument about maintainability Finding repeat sequences..