sub gen_hamm { return [] unless @_; my $x = shift; my $out; $out = merge( ll_new( 1, memoize( sub { ll_map( sub { $x * $_[ 0 ] }, $out ); } ) ), gen_hamm( @_ ) ); return $out; } sub merge { my ( $x, $y ) = @_; return $y if ll_null_p( $x ); return $x if ll_null_p( $y ); my ( $x0, $y0 ) = map head( $_ ), ( $x, $y ); if ( $x0 < $y0 ) { return ll_new( $x0, memoize( sub { merge( tail( $x ), $y ) } ) ); } elsif ( $y0 < $x0 ) { return ll_new( $y0, memoize( sub { merge( $x, tail( $y ) ) } ) ); } else { return ll_new( $x0, memoize( sub { merge( tail( $x ), tail( $y ) ); } ) ); } } sub ll_map { my $proc = shift; my $s = shift; if ( ll_null_p( $s ) ) { return []; } else { ll_new( $proc->( head( $s ) ), memoize( sub { ll_map( $proc, tail( $s ) ) } ) ); } } sub take { my $n = shift; my $s = shift; return $n < 1 ? () : ( head( $s ), take( $n - 1, tail( $s ) ) ); } sub memoize (&) { my $proc = shift; my $already_run = 0; my $result; return sub { return $result if $already_run; $already_run = 1; return $result = $proc->(); } } sub ll_new { [ @_[ 0, 1 ] ]; } sub force { my $sub = shift; $sub->(); } sub head { shift->[ 0 ]; } sub tail { force( shift->[ 1 ] ); } sub ll_null_p { !@{ $_[ 0 ] }; } sub element_wise { my $op = shift; my ( $s1, $s2 ) = @_; ll_new( $op->( head( $s1 ), head( $s2 ) ), memoize( sub { element_wise( $op, tail( $s1 ), tail( $s2 ) ); } ) ); } sub ll_add { element_wise( sub { $_[ 0 ] + $_[ 1 ] }, @_ ); }