package ToolBox; use AutoCurry ':all'; use List::Util qw( min ); # zips sub zip { my $len = min( map scalar @$_, @_ ); map [ do { my $i=$_; map $_->[$i], @_ } ], 0..$len-1; } sub zip_with { my $f = shift; map $f->(@$_), zip(@_); } # folds & scans sub foldl { my $f = shift; my $z = shift; $z = $f->($z, $_) for @_; $z; } # (other folds and scans omitted) # functional glue: curry and compose sub _compose2 { my ($f, $g) = @_; sub { $f->($g->(@_)) } } sub compose { foldl( \&_compose2, @_ ); } sub pipeline { compose( reverse @_ ); } # a partially-applicable map sub map_c(&@) { my $f = shift; my $args = \@_; sub { map $f->($_), @$args, @_; } } #### use ToolBox; use Data::Dumper; # a convenience function for examining our output sub say(@) { local $Data::Dumper::Terse = 1; local $Data::Dumper::Indent = 0; my @d = map Dumper($_), @_; print "@d$/"; } #### # some simple binary operators sub plus { $_[0] + $_[1] } sub times { $_[0] * $_[1] } # build n-ary operators from them *sum = foldl_c( \&plus, 0 ); *product = foldl_c( \×, 1 ); # test them out say sum(1..10); # 55 say product(1..10); # 3628800 #### *dot_product = compose( \&sum, zip_with_c( \&product )); say dot_product( [1,1,1], [1,2,3] ); # 6 #### *combos = foldr_c( \&outer_prod, [[]] ); sub outer_prod { my ($xs, $ys) = @_; [ map do { my $x=$_; map [$x, @$_], @$ys }, @$xs ]; } say combos( ['a','b'], [1,2,3] ); # [['a',1],['a',2],['a',3],['b',1],['b',2],['b',3]] #### *powerset = pipeline( map_c { [ [$_], [] ] }, # step 1 \&combos, # step 2 map_c { map [ map @$_, @$_ ], @$_ } # step 3 ); say powerset( 1, 2 ); # [1,2] [1] [2] [] say powerset(qw( a b c )); # ['a','b','c'] ['a','b'] ['a','c'] ['a'] # ['b','c'] ['b'] ['c'] []