Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrap perl's sort function in an object

I'm trying to make a sort function available in one of my (object-oriented) packages that accepts a block and makes available $a and $b like the standard Perl sort.

First, a simplified version of what I'm trying to do in the package that contains the wrapped sort function:

# In package My::Object
sub sort {
  my $self = shift;
  my $block = \&{shift @_};

  return sort $block @{$self->{arrayRef}}; # I want to use the passed in block with array data contained in this object
}

And then an example of a client passing a block which defines the comparitor to run for the sort:

my $obj = My::Object->new([3, 1, 5, 6, 2, 4]); # As an example, these values will be come arrayRef from above
my @sortedVals = $obj->sort({ $a < $b });

Is there a way to do what I'm trying to do while still being able to use Perl's sort?

like image 426
konapun Avatar asked Jan 21 '15 03:01

konapun


People also ask

How to sort a list in Perl?

We can specify sort function in the form of subroutine or blocks in Perl, if we have not defined specified block or subroutine then sort function will take default method of sorting. The sort function sorts a list and returns the sorted list as the output to the user. Below is the syntax of the sort function in Perl is as follows.

What is the use of sort function in Python?

The sort function sorts a list and returns the sorted list as the output to the user. We can specify this function in the form of subroutine or blocks, if we have not defined a specified block or subroutine then sort function will take the default method of sorting.

What is a Perl function?

In general, the Perl function is defined as a function containing subcodes or a logical set of code to perform some task and can be reused in the program. In this article, we will see how to declare and use the function in Perl programming.

How can I sort the list returned from a function?

Historically Perl has varied in whether sorting is stable by default. If stability matters, it can be controlled explicitly by using the sort pragma. Warning: syntactical care is required when sorting the list returned from a function. If you want to sort the list returned by the function call find_records (@key), you can use:


1 Answers

Mostly.

To use the bare-block-as-subroutine syntax, you need to use the & prototype. Generally you should avoid prototypes, but passing a subroutine as a bare block is one of the few times this is acceptable. Unfortunately, because they must be determined and applied at compile time, prototypes do not work on methods. So you have to use the full anonymous subroutine syntax, sub { ... }.

my @sortedVals = $obj->sort(sub { $a <=> $b });

$a and $b are globals of the package the sort subroutine was declared in (let's say this is Some::Caller). When run inside your class, sort will set $My::Object::a and $My::Object::b but the subroutine will be looking for $Some::Caller::a and $Some::Caller::b. You can work around this by aliasing their $a and $b to your $a and $b.

sub sort {
    my $self = shift;
    my $block = shift;

    no strict 'refs';
    local *{caller.'::a'} = *a;
    local *{caller.'::b'} = *b;

    return sort $block @{$self->{arrayRef}};
}

local makes a temporary copy of a global for the duration of the block and this includes other subroutines called, so this will effect the sort. Then when the method is done the value will revert to what it was.

Perl globals are stored in typeglobs which contain all the global variables with the same name. *a contains $a and @a and %a. By copying the caller's typeglob, when $My::Object::a changes, the caller's $a will change as well. They are aliases.

The *{...} syntax lets you get at a global variable by name using another variable or expression. This is called a symbolic reference. Now we can get at the caller's *a. Using this syntax is usually a mistake, so you have to turn off strict else Perl won't let you do it.

like image 135
Schwern Avatar answered Oct 14 '22 16:10

Schwern