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'] []