NAME

Sort::Search::Cookbook - practical examples for Sort::Search

SYNOPSIS

This is a collection of quick references and examples on how to use Sort::Search. It is not expected that you read this document linearly, although the first two sections here are introductory in nature, and are thus understood to have been read.

As a general rule of thumb, any existing binary search function most definitely translates to a "left search" here. Despite examples are provided for both, keep in mind that "right search" is unconventional (at least to my knowledge) and can be counterintuitive.

DEFINITION

The terms "predicate", "ordering", "search range" are used in the same sense as they are defined in "Terminology" in Sort::Search. In addition, assume these variables mean the following:

$_

The current element during a search.

$idx

The array index result found by a search. The index may not correspond to a real element in the array, in which case the following two variables would be undef.

$cmp

The comparison result returned by the predicate/ordering on the found element. If there is no such element, this is undef.

$elp

The scalar ref to the found element, or undef if none was found.

You import the appropriate functions into your current namespace like this:

use Sort::Search qw( blsrch0 blsrch1 );
       # imports `blsrch0', `blsrch1'

... you get the point. If you need more just add more to the list. :)

EXAMPLES

Exact match

The second field $cmp can be used to determine (1) if the return index corresponds to a real array element and (2) if the return index corresponds to an exact match.

For (1), check $cmp is defined. For (2), check if $cmp equals zero, which is equivalent to checking $array->[$idx] <=> $want, but it save you an extra comparison. If you also need the array element, use the third field $elt, which is equivalent to $array->[$idx].

Concretely, given the numerically sorted array:

my @array = qw( 12 17a 17b 17c 21 );

and the numeric ordering:

                      (-) $_  precedes $want
sub { $_ <=> $want }  (0) $_  equals   $want
                      (+) $_  follows  $want

you can find the first match with blsrch0:

sub lsearch {
    my ($array, $want) = @_;
    my ($idx, $cmp, $elp) = blsrch0 { $_ <=> $want } $array;
    if (!defined $cmp) { print "$want not found anywhere\n"; }
    elsif ($cmp != 0)  { print "$want not found; best "
                             . "is $$elp at [$idx]\n"; }
    else               { print "$want found at $$elp [$idx] :)\n"; }
}

lsearch \@array => 11;  # 11 not found; best is 12 at [0]
lsearch \@array => 12;  # 12 found at 12 [0] :)
lsearch \@array => 17;  # 17 found at 17a [1] :)
lsearch \@array => 18;  # 18 not found; best is 21 at [4]
lsearch \@array => 99;  # 99 not found anywhere

and the last match with brsrch0:

sub rsearch {
    my ($array, $want) = @_;
    my ($idx, $cmp, $elp) = brsrch0 { $want <=> $_ } $array;
    if (!defined $cmp) { print "$want not found anywhere\n"; }
    elsif ($cmp != 0)  { print "$want not found; best "
                             . "is $$elp at [$idx]\n"; }
    else               { print "$want found at $$elp [$idx] :)\n"; }
}

rsearch \@array => 11;  # 11 not found anywhere
rsearch \@array => 12;  # 12 found at 12 [0] :)
rsearch \@array => 17;  # 17 found at 17c [3] :)
rsearch \@array => 18;  # 18 not found; best is 17c at [3]
rsearch \@array => 99;  # 99 not found; best is 21 at [4]

Notice that the ordering is reversed: $want <=> $_ instead of $_ <=> $want, due to using a right search instead of a left search. The conventional left search may be used, though $cmp and $elp would be meaningless, thus you must check and retrieve the element separately:

sub rsearch {
    my ($array, $want) = @_;
    my $idx = blsrch1 { $_ <=> $want } $array;
    --$idx;
    if ($idx < 0) {
        print "$want not found anywhere\n";
    } else {
        my $elt = $array->[$idx];
        my $cmp = $elt <=> $want;
        print $cmp == 0
        ? "$want found at $elt [$idx] :)\n"
        : "$want not found; best is $elt at [$idx]\n";
    }
}

The output of this rsearch is identical to above.

To get all matches at once, use blsrch2 or brsrch2:

sub srchall {
    my ($lo, $hi);
    my ($array, $want) = @_;

    # EITHER:
    ($lo, $hi) = blsrch2 { $_ <=> $want } $array;
    print "[$_]=$array->[$_] " for ($lo .. $hi-1);

    # OR:
    ($hi, $lo) = brsrch2 { $want <=> $_ } $array;
    print "[$_]=$array->[$_] " for ($lo+1 .. $hi);
}

srchall \@array => 11;  # <empty>
srchall \@array => 12;  # [0]=12
srchall \@array => 17;  # [1]=17a [2]=17b [3]=17c
srchall \@array => 21;  # [4]=21
srchall \@array => 99;  # <empty>

Note that the left search range is right-exclusive and the right search is left-exclusive.

CONVERSION GUIDE

From (other) CPAN modules:

SEE ALSO

Sort::Search, the main documentation.