views:

66

answers:

2

I am inserting 2-dimensional array references into my heap in Perl. How should I define the 'elements' attribute when constructing my heap so that I can properly use my comparator function?

my $heap = Heap::Simple->new( order     => \&byNumOrStr,
                              elements  => [Array => 0]
                             );

sub byNumOrStr
{
    my ( $a, $b ) = @_;

    $b->[0] <=> $a->[0]  #0-th element is a number. 
            ||
    $a->[1] cmp $b->[1]; #1-st element is a number
}

I keep getting back this error:

Can't use string ("2.55") as an ARRAY ref while "strict refs" in use ... (This means I might actually have to compare my "number string" numerically)

A: 

Well, it's likely that either $a or $b is being passed in as a string. Try printing out this variables after the assignment.

From what I can see from the documentation, when you pass elements => [ Array => 0 ], unless the 0th item in the array is an array then you'll only be comparing the values in the first slot of the array.

[Array => $index]
Indicates that the elements are array references, with the key at index $index. So now the element can be not just the key, but also associated data.

This means that if 2.55 is in the array like [ 2.55, ... ] then that's what's being passed in as $a or $b.

The elements entry tells H::S how you want to derive the key. For a completely generic way, it says that you can pass [Function => $code_ref_for_key]. You could make it like this:

sub first_two_slots { my $array_ref = shift; return [ @$array_ref[0,1] ]; }

And then with the order as specified, it would pass that array into your order and specify

my $heap = Heap::Simple->new( order     => \&byNumOrStr,
                              elements  => [Function => \&first_two_slots]
                             );

Original comment left in place: (It's not relevant to how Heap::Simple calls order).

if byNumOrStr is called from sort DON'T assign $a and $b in it. Those values are set by sort. If there is something coming in @_ it's probably not what you want.

Axeman
Those are different variables anyway, since sort's $a and $b are package veriables
Leon Timmermans
@Leon Timmermans, I don't think it matters. If he lexicalizes `$a` and `$b` then those are the ones it's going to try to reference as arrays, not the ones in the symbol table.
Axeman
I don't think `$a` and `$b` can be lexicalized. `perldoc perlvar` says: "Special package variables when using sort(), see "sort" in perlfunc. Because of this specialness $a and $b don't need to be declared (using use vars, or our()) even when using the "strict 'vars'" pragma. Don't lexicalize them with "my $a" or "my $b" if you want to be able to use them in the sort() comparison block or function."
Ether
You can lexicalize `$a` and `$b` but afterward, when sort tries to localize them it is going to blow up with `Can't use "my $a" in sort comparison.` You can use `$::a` and `$::b` to get around it, but best not to lexicalize them in the first place
Eric Strom
When passing the function to the elements entry, are we creating new array references to be inserted into the Heap? I am inserting many, many of these references into my Heap and I'm noticing a 700% increase in runtime. Can I do anything to optimize it? Creating new question in a couple of mins....
syker
A: 

Sorting a two-dimensional array doesn't really make sense -- when you sort something, there is a defined order. Having two sort criteria doesn't make it a two-dimensional list... do you mean that you have data that are a list of two elements? e.g.:

my $element = [ '0', 'string' ];

I think Example 1 in the documentation ("where key and value are kept separate") applies here -- you want to sort the references, not the values themselves. So try declaring with elements => "Any", and then adjust your sort method to match:

(I was wrong.. it looks like elements => [Array => 0] is correct, since these are just plain old arrayrefs being sorted.

my $heap = Heap::Simple->new( order     => \&byNumOrStr,
                              elements  => [Array => 0],
                             );

sub byNumOrStr
{
    my ( $val1, $val2 ) = @_;

    my $result = 
        $val1->[0] <=> $val2->[0]  # the 0th element is a number
                    ||
        $val1->[1] cmp $val2->[1]; # the 1st element is a string

    # The docs say "this should return a true value if $key1 is smaller than $key2 and a false value otherwise."
    return $result == -1;
}

PS. As discussed in http://stackoverflow.com/questions/3146484/secondary-order-in-heapsimple/3146526#3146526, the comparison function in Heap::Simple does not want a return value of -1, 0, or 1, but rather true or false. You need to convert the comparison result before returning from the function.

Ether
Yes my data is a list of two elements, or rather, a reference to that list.
syker
Also why is it that you don't need to specify a return 0?
syker
@syker: the docs say "This should return a true value if $key1 is smaller than $key2 and a false value otherwise." -- the return value of a function is the last expression evaluated, which is the comparison to -1. I can make that a little more clear in the code, which I'll do now.
Ether