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

I'm trying to select the maximum element from a list according to some arbitrary criteria.

Basically what I'd like to be able to do is something like

$max_el = max { complex_calculation($_) } @list;

and have $max_el be the element for which complex_calculation reported the highest value

Unfortunately the max in List::Util doesn't take a block.

I was able to cobble something together with List::Util::reduce but this seems less than ideal.

$max = reduce { complex_calculation($a) > complex_calculation($b) ? $a + : $b };

My problems with this approach are
1.) complex_calculation() is called twice for each element in the list
2.) the code is less readable & less concise than it could be
3.) this seems like I'm reinventing the wheel.

Does anyone know of a CPAN module or library that will encapsulate this type of functionality?

I've googled by couldn't find anything.

Thanks,

Replies are listed 'Best First'.
Re: List::Util max with block
by ikegami (Patriarch) on Nov 06, 2009 at 19:39 UTC
    my ($max) = map $_->[0], reduce { $a->[1] > $b->[1] ? $a : $b } map [ $_, complex_calculation($_) ], LIST;

    Update: Added missing parens around LHS of assignment

      Alternative:
      my $max = shift(@list); my $max_c = complex_calculation($max); for (@list) { my $c = complex_calculation($_); if ($c > $max_c) { $max = $_; $max_c = $c; } }
        Thanks ikegami,

        These are both excellent suggestions.
Re: List::Util max with block
by keszler (Priest) on Nov 06, 2009 at 20:27 UTC
    If you wanted the entire @list in sorted order, the Schwartzian Transform would be what you're looking for:
    @sorted_els = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, complex_calculation($_)] } @list;

    I haven't tested the efficiency, but you might want to compare the other solutions with:

    # swapped $a/$b to get max ($max_el) = map { $_->[0] } sort { $b->[1] <=> $a->[1] } map { [$_, complex_calculation($_)] } @list;

      Why do you call map { $_->[0] } on every element if you just want one?

      Note that the map EXPR, syntax of map is faster than the map BLOCK syntax.

      And then there's the issue that you've taken an O(N) problem and made it O(N log N) by using sort, which means it doesn't scale as well.

Re: List::Util max with block
by toolic (Bishop) on Nov 06, 2009 at 19:38 UTC
    Can't you use map?
    $max_el = max( map { complex_calculation($_) } @list);
      The OP wants the value that produced that maximum calculation result, not the maximum calculation result itself.