http://qs321.pair.com?node_id=1071519

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

Hi Monks, Is this possible? I have an object with a myList element. I would like to provide a client who uses the object the ability to use their own sorting routine. If they don't provide one, it fall backs to an internal sort routine. When I try to run my script, however, the fallback works as expected, but the $a and $b internal variables are undefined (they are not elements of $myList) in the client callback. Am I doing something wrong here or is this not possible? Thanks in advance.
##Object: sub _byCode { $a->asStringFmt() cmp $b->asStringFmt; } my $callback = $self->{sortCallback} || "_byCode"; ... foreach my $key (sort $callback @{$myList->getKeys}) { ... }
##Client: my $byCodeAndX = sub { .... }; my $obj = new Obj( sortCallback => $byCodeAndX )

Replies are listed 'Best First'.
Re: providing a callback routine to sort ( $::a $::b )
by Anonymous Monk on Jan 21, 2014 at 21:53 UTC

    Well, ... isn't sufficient for reproducing

    Anyway the error message I get is Use of uninitialized value $Foo::a in numeric comparison (<=>) at - line 2.

    $Foo::a is not the same as $main::a aka $::a aka perlvar#$a
Re: providing a callback routine to sort
by pemungkah (Priest) on Jan 21, 2014 at 22:04 UTC
    Just did this the other day. The key is to remember that sort's curly block is essentially an anonymous sub, so what you put there has to be executable code. In the case of a sort subroutine, this means you need to do something like this:
    my $dir = shift; my $sort_sub = $dir eq 'up' ? sub {$a <=> $b} : sub {$b <=> $a}; my @items = (3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5); print "@{[sort { $sort_sub->() } @items]}\n";
    Which gives us
    $ perl sample up 1 1 2 3 3 4 5 5 5 6 9 $ perl sample down 9 6 5 5 5 4 3 3 2 1 1
    Note that the code in the sort curlies calls the callback itself.
        So it is - thanks! I'm just so used to having the curlies in there that I'd forgotten I could drop them.
Re: providing a callback routine to sort
by Anonymous Monk on Jan 21, 2014 at 23:01 UTC
    In Perl, "a reference" can be a reference to anything, including a subroutine (ref($whatever) eq 'CODE' ...) So, you certainly could provide sort with a subroutine that checks if $whatever refers to a subroutine, and if so, calls it ... otherwise taking some default action. . .

    Notice how the sort routine, within your code, has only one clear subroutine to call. That subroutine, however, is faced with a decision.
Re: providing a callback routine to sort
by Laurent_R (Canon) on Jan 21, 2014 at 23:26 UTC
    I really think you did not provide enough information to help you, but this:
    $a->asStringFmt() cmp $b->asStringFmt;
    looks probably wrong to me. Left side looks like a function, right side not.

      I really think you did not provide enough information to help you

      See Re: providing a callback routine to sort ( $::a $::b )

      The only reason $a and $b are undefined is because they're not $main::a and $main::b -- sort deals with $main::a and $main::b or $::a for sort -- this is the OPs problem and the solution, use the right variable (package affects unqualified variables)

      sort talks about this exactly

      An alternate solution (using prototypes) is also talked about in sort and in chromatics free book Modern Perl a loose description of how experienced and effective Perl 5 programmers work....You can learn this too.

      looks probably wrong to me. Left side looks like a function, right side not.

      Well, they're both perfectly identical method calls

        Thank you! I provided $pkgdir::pkg::a and $pkgdir::pkg::b and it worked! My apologies for missing that in the documentation. Unfortunately, it's all the way at end (in very tiny letters ;)