Toby Inkster avatar Toby Inkster committed 2612bd2

NodeList->reduce; also add check for whether parameter is a coderef.

Comments (0)

Files changed (2)

lib/XML/LibXML/NodeList.pm

 
 sub map {
     my $self = CORE::shift;
-    my $sub  = CORE::shift;
+    my $sub  = __is_code(CORE::shift);
     local $_;
     my @results = CORE::map { @{[ $sub->($_) ]} } @$self;
     return unless defined wantarray;
 
 sub grep {
     my $self = CORE::shift;
-    my $sub  = CORE::shift;
+    my $sub  = __is_code(CORE::shift);
     local $_;
     my @results = CORE::grep { $sub->($_) } @$self;
     return wantarray ? @results : (ref $self)->new(@results);
 
 sub sort {
     my $self = CORE::shift;
-    my $sub  = CORE::shift;
+    my $sub  = __is_code(CORE::shift);
     my @results = CORE::sort { $sub->($a,$b) } @$self;
     return wantarray ? @results : (ref $self)->new(@results);
 }
     return $self;
 }
 
+sub reduce {
+    my $self = CORE::shift;
+    my $sub  = __is_code(CORE::shift);
+    
+    my @list = @$self;
+    CORE::unshift @list, $_[0] if @_;
+    
+    my $a = CORE::shift(@list);
+    foreach my $b (@list)
+    {
+        $a = $sub->($a, $b);
+    }
+    return $a;
+}
+
+sub __is_code {
+    my ($code) = @_;
+    
+    if (ref $code eq 'CODE') {
+        return $code;
+    }
+    
+    # There are better ways of doing this, but here I've tried to
+    # avoid adding any additional external dependencies.
+    #
+    if (UNIVERSAL::can($code, 'can')        # is blessed (sort of)
+    and overload::Overloaded($code)         # is overloaded
+    and overload::Method($code, '&{}')) {   # overloads '&{}'
+        return $code;
+    }
+    
+    die "Not a subroutine reference\n";
+}
+
 1;
 __END__
 
 the list. Similar to C<map>, but instead returning the list of values
 returned by $coderef, returns the original NodeList.
 
+=head2 reduce($coderef, $init)
+
+Equivalent to List::Util's reduce function. C<$init> is optional and
+provides an initial value for the reduction.
+
+Caveat: Perl's magic C<$a> and C<$b> variables are not available in
+C<$coderef>. Instead the two terms are passed to the coderef as arguments.
+
 =cut
 use strict;
 use warnings;
 
-use Test::More tests => 23;
+use Test::More tests => 25;
 
 use XML::LibXML;
 use IO::Handle;
 # TEST
 is(join('|',@$reverse), '10|9|8|7|6|5|4|3|2|1', 'foreach works');
 
+my $biggest  = $shuffled->reduce(sub { $_[0] > $_[1] ? $_[0] : $_[1] }, -1);
+my $smallest = $shuffled->reduce(sub { $_[0] < $_[1] ? $_[0] : $_[1] }, 9999);
+
+# TEST
+is($biggest, 10, 'reduce works 1');
+
+# TEST
+is($smallest, 1, 'reduce works 2');
 
 # modified version of Scalar::Util::PP::refaddr
 # only works with blessed references
-sub blessed_refaddr($) {
+sub blessed_refaddr {
   return undef unless length(ref($_[0]));
   my $addr;
   if(defined(my $pkg = ref($_[0]))) {
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.