in reply to How to get the index of smallest whole number in an array?

What I find interesting, and I hope I am not out of my depth here, please correct, is that in one of the four* (?) approaches to a solution, the so-called Schwartzian transform (ST) is used. I think (as I learned about this trick not that long ago) I have spotted ST on tybalt89's, kcott's and AnomalousMonk's solutions.

What I mean is that sort() is passed something different than just the original array or just its indices in the form of additional/pre-processed information. In this case it is the index of each element *as well* as its value in the form of a tuple-ref. E.g. where map passes on to sort [ $_ => $x[$_] ] or () (another trick I learned when you want map to filter(=grep) too). And so, sort does not have to constantly ask for $arr[$a], instead it is given this value and does $a->[1] (which is comparable in overhead but let's ignore this for the sake of argument).

On the other hand, johngg manages without ST because it passes only the index of each element after filtering out non-whole numbers. And so sort() does this: $arr[$a] <=> $arr[$b].

I am not posting any benchmarks because they are totally irrelevant as this is a pet-scenario toy problem.

Now, consider that the problem was the following: For the same array, find the index of the element with the highest prime factor.

This is a blindingly obvious case for the use of ST because there is no contest between:

sort { $a->[1] <=> $b->[1] } map { $arr[$_] >= 0 ? [ $_, max_prime_factor($arr[$_] +) ] : () } 0 .. $#arr

and

sort { max_prime_factor($arr[$a]) <=> max_prime_factor +($arr[$b]) } grep { $arr[ $_ ] >= 0 } 0 .. $#arr

----

four* : 1) swartzian map/sort, 2) non-swartzian map/sort, 3) List::Util et al, 4) PDL

Replies are listed 'Best First'.
Re^2: How to get the index of smallest whole number in an array?
by jdporter (Paladin) on Jul 02, 2018 at 16:01 UTC

    Exactly. This is and has always been the whole rationale for the ST: when the comparands are expensive to calculate, calculate them at most once!

Re^2: How to get the index of smallest whole number in an array?
by BrowserUk (Patriarch) on Jul 02, 2018 at 16:22 UTC

    Using an O(NlogN) sort instead of a O(N) linear search to find a maximum or minimum value of a list will always be inefficient.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit

      Now that you mention it I agree with you, though I have not realised it earlier. Sorting is an overkill when just min value is required. But hey we cover every angle in our homework service :)

      In which case your List::Util::reduce solution or a basic min-find will suffice:

      $idx = $arr[0]; for(0..$#arr) { $idx = $_ if( ($arr[$idx]<0) || (($arr[$_] >= 0) && ($ +arr[$_] < $arr[$idx])) ) }
        $idx = $arr[0]; for(0..$#arr) { $idx = $_ if( ($arr[$idx]<0) || (($arr[$_] >= 0) && ($arr[$_] +< $arr[$idx])) ) }

        This returns strange results for certain corner cases:

        c:\@Work\Perl\monks>perl -wMstrict -le "use Data::Dump qw(pp); ;; for my $ar ( [ ], [ -1 ], [ -99 ], [ 0, -99 ], [ -99, 0 ], ) { my $idx = $ar->[0]; for (0..$#$ar) { $idx = $_ if( ($ar->[$idx]<0) || (($ar->[$_] >= 0) && ($ar->[$_] +< $ar->[$idx])) ); } print qq{(@$ar) -> lnn index of }, pp $idx; } " () -> lnn index of undef (-1) -> lnn index of 0 Use of uninitialized value in numeric lt (<) at -e line 1. (-99) -> lnn index of -99 (0 -99) -> lnn index of 0 Use of uninitialized value in numeric lt (<) at -e line 1. Use of uninitialized value in numeric lt (<) at -e line 1. Use of uninitialized value in numeric lt (<) at -e line 1. (-99 0) -> lnn index of -99
        I'd hate to have to extend this to dealing with fractional numbers. The O(n) solutions here and here both return an unambiguous undef for lists containing no LNN, including empty lists.


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

        I have voted only for your solution. Because while everyone is showing solutions with sort, map grep. I just don't understand that after 30 posts finally someone is talking some sense.

        If I would have needed to solve something like this, I would have come up with a simple loop. Job done, move on:

        use strict ; use warnings ; my @arr = ( 3, 4, 71, 1, 1.5, -598, -100203, 0.5, -2, -1.5 ); my $result ; while (my ($ix, $val) = each @arr) { next if ( $val < 0 || int $val != $val || ($result && $val > $resu +lt->[0]) ) ; $result = [ $val, $ix ] ; } if ( $result ) { print $result->[0] . " at " . $result->[1] . "\n" ; } __END__ 1 at 3