Here is a lazy-list example of merge, with one of its arguments being a finite list, and the other an infinite list. I may not get around to figuring out the Hamming generator, but I think the techniques I've employed here tell you how it can be done.
use strict;
use warnings;
# Merge takes two iterators, which can be called
# with no arguments, in which case they iterate, returning
# their next value; or with a string 'peek', which will
# yield the next value without effectively shifting it.
# Exhausted iterators return undef on all subsequent calls
#
# Merge itself is an iterator, returning the next element in
# the merged series, plus a continuation coderef
sub merge {
my ($a, $b) = @_;
my ($car_a, $car_b) = ($a->('peek'), $b->('peek'));
# Base case: if one of them is empty, return the other
defined $car_a or return ($b->(), sub {merge($a, $b)});
defined $car_b or return ($a->(), sub {merge($a, $b)});
# Pull off lesser (both if equal) first element(s)
my $low_car;
if ($car_a <= $car_b) { $low_car = $a->() }
if ($car_b <= $car_a) { $low_car = $b->() }
return ($low_car, sub {merge($a, $b)} );
}
my $I1 = do {
my @arr = (1..10);
sub {
if (@arr == 0) {
return undef;
}
elsif (@_) { # should be peek, but any other arg works (undocu
+mented)
return $arr[0];
}
else {
return shift @arr;
}
}
};
my $I2 = do {
my $i = 2;
sub {
if (@_) {
return $i;
} else {
my $i_was = $i; $i += 2; return $i_was;
}
}
};
my $iterations_to_print = 30;
for ( my ($elem, $cont) = merge($I1, $I2);
$iterations_to_print-- > 0;
($elem, $cont) = $cont->()
) {
print "<$elem>\n";
}
Caution: Contents may have been coded under pressure.