FunctionalPerl
view release on metacpan or search on metacpan
lib/FP/Trie.pm view on Meta::CPAN
use FP::Stream;
use Scalar::Util qw(blessed);
use FP::Carp;
use FP::Struct ["sublevels"], "FP::Trie::Trie";
sub perhaps_value { () }
sub perhaps_skip {
@_ == 2 or fp_croak_arity 2;
my ($t, $l) = @_;
my ($t2, $maybe_l2) = $t->skip($l);
defined $maybe_l2 ? () : ($t2)
}
our $key_not_found_exception = FP::Trie::KeyNotFoundException->new;
sub perhaps_ref {
@_ == 2 or fp_croak_arity 2;
my ($t, $l) = @_;
if (my ($t2) = $t->perhaps_skip($l)) {
$t2->perhaps_value
} else {
()
}
}
*maybe_ref = perhaps_to_maybe \&perhaps_ref;
*ref_or = perhaps_to_or \&perhaps_ref;
*ref = perhaps_to_x \&perhaps_ref, $key_not_found_exception;
*exists = perhaps_to_exists \&perhaps_ref;
# returns ($ending_level, $maybe_keyremainder,
# $maybe_lastvaluelevel, $maybe_keyremainder_lvl)
sub skip {
@_ == 2 or @_ == 4 or fp_croak_arity "2 or 4";
my ($t, $l, $maybe_lastvaluelevel, $maybe_keyremainder_lvl) = @_;
my ($maybe_lvl, $maybe_r_lvl)
= (defined blessed $t)
&& $t->isa("FP::Trie::ValueLevel")
? ($t, $l)
: ($maybe_lastvaluelevel, $maybe_keyremainder_lvl);
if ($l->is_null) {
# found the node, which is perhaps holding a value
($t, undef, $maybe_lvl, $maybe_r_lvl)
} else {
my ($a, $l2) = $l->first_and_rest;
if (my ($t2) = hash_perhaps_ref($$t{sublevels}, $a)) {
# XX TCO
$t2->skip($l2, $maybe_lvl, $maybe_r_lvl)
} else {
# no value for the full key; $t is the last seen
# level, $l the remainder of the key
($t, $l, $maybe_lvl, $maybe_r_lvl)
}
}
}
sub update {
@_ == 3 or fp_croak_arity 3;
my ($t, $l, $fn) = @_;
if ($l->is_null) {
FP::Trie::ValueLevel->new($$t{sublevels}, &$fn($t->perhaps_value))
} else {
my ($a, $l2) = $l->first_and_rest;
$t->sublevels_update(
sub {
hash_update $_[0], $a, sub {
do {
if (my ($t2) = @_) {
$t2
} else {
$FP::Trie::empty_trie
}
}
->update($l2, $fn)
}
}
)
}
}
sub xdelete {
@_ == 2 or fp_croak_arity 2;
my ($t, $l) = @_;
if ($l->is_null) {
if ((defined blessed $t) && $t->isa("FP::Trie::ValueLevel")) {
if (keys %{ $$t{sublevels} }) {
FP::Trie::BareLevel->new($$t{sublevels})
} else {
# equivalent but detectable to be empty from outer
# layers
$FP::Trie::empty_trie
}
} else {
die $key_not_found_exception
}
} else {
my ($a, $l2) = $l->first_and_rest;
$t->sublevels_update(
sub {
hash_update $_[0], $a, sub {
if (my ($t2) = @_) {
my $t3 = $t2->xdelete($l2);
$t3 eq $FP::Trie::empty_trie ? () : $t3
} else {
#()
# When does this happen? When the key goes
# past the existing tree.
die $key_not_found_exception
}
}
}
)
}
}
( run in 2.843 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )