in reply to Is it possible to sort using a coderef, without first storing the coderef in a scalar

It is legal to use a coderef with sort, but if you use a named sub, that sub is expected to return -1, 0, or 1. If it returns anything non-numeric such as a coderef, you get a fatal.

Your first example calls get_numeric() once, assigning the coderef it returns to $sorter. $sorter, containing a coderef, has its referant code executed by sort. But get_numeric() is only called once, before the sort routine is ever invoked. This is an important distinction; $sorter contains a coderef, that when executed returns -1, 0, or 1.

In your second example, you should actually write it like this:

my @sorted2 = sort get_numeric (3,2,1);

...because that's syntactically correct. sort doesn't want "get_numeric()", it wants subname, which is "get_numeric" (without the parens). But when you compose it as I've demonstrated above, you get a different error altogether. You get "Subroutine didn't return a numeric value..." Why? Because get_numeric() doesn't return a numeric value... instead it returns a coderef pointing to numeric(). That's an additional level of indirection that sort isn't equipped to dive into once it sees that it's been given a subname.


Dave

  • Comment on Re: Is it possible to sort using a coderef, without first storing the coderef in a scalar
  • Download Code

Replies are listed 'Best First'.
Re^2: Is it possible to sort using a coderef, without first storing the coderef in a scalar
by imp (Priest) on Jul 26, 2006 at 05:19 UTC
    The part that I'm not quite sure about is the distinction between these two:
    # Doesn't work sort \&numeric @list; # Works my $sorter = \&numeric; sort $sorter @list;
    I know that the first example would work if I used numeric instead of \&numeric - but why does \&numeric not also work?

    I'm playing with a module that defines a set of common sort patterns, and provides methods that return coderefs that implement the pattern that was requested.

    For example, sorting a list of hashes based on the value of a user specified key. The custom implementation would be something like:

    sub sort_hashkey($$) { my ($a,$b) = @_; $a->{foo} <=> $b->{foo}; } my @sorted = sort_hashkey @list;
    My module provides this:
    my $sorter = sorthash_numeric('foo'); my @sorted = sort $sorter @list;
    It would be nice, but not neccesary, to do this:
    my @sorted = sort sorthash_alpha('foo') @list;

      If you wish to use that syntax, you could use a wrapper.

      sub mysort { my $sorter = shift; return sort $sorter @_; } my @sorted = mysort sorthash_alpha('foo'), @list;

      Because you have a choice according to the docs for sort. You can use a block, a subname, or (from the docs): "SUBNAME may be a scalar variable name (unsubscripted), in which case the value provides the name of (or a reference to) the actual subroutine to use." There is nothing in the docs that says you can use a subroutine call to return a sub reference. You can use a block, a subname, or a scalar variable containing the name of a sub or a reference to a sub. Nothing else. And your sub named in 'subname' must return an integer (-1, 0 or 1, for example). Likewise, the sub referred to in $subname must return an integer.


      Dave