#!/usr/bin/perl use strict; use warnings; use constant HEAD => 0; use constant TAIL => -1; # Usage my $next = genHam( take => #, list => [] ); # If take is not specified, it assumes infinity my $next = genHam( take => 23, list => [2, 3, 5] ); while ( my $n = $next->() ) { print "$n\n"; } sub genHam { die "Incorrect number of arguments" if @_ % 2; my %arg = @_; return () if ! exists $arg{list} || ref $arg{list} ne 'ARRAY'; $arg{take} ||= -1; my $n = 0; my (@pool, @stream); push @stream, [] for 0 .. $#{ $arg{list} }; $stream[TAIL] = [ $arg{list}->[TAIL] ]; return sub { return () if ! $arg{take}--; # Return obligatory 1 return ++$n if ! $n; # No need to generate as we already have some in the pool return shift @pool if @pool; # Increase highest factor $stream[TAIL] = [ $arg{list}->[TAIL] * $n ]; ++$n; # Generate streams of multiples >= highest factor for my $i ( 0 .. $#stream - 1 ) { # Start with empty stream $stream[ $i ] = []; my $multiple = $arg{list}->[ $i ]; # Determine what multiple each factor should start at my $start = $n > 2 ? ($arg{list}->[TAIL] * ($n - 2)) / $multiple + 1 : 1; # Add multiples to the stream less than or equal to the highest factor for ( $start .. $stream[TAIL]->[HEAD] / $multiple ) { push @{ $stream[ $i ] }, $multiple * $_; } } # Merge streams into new pool @pool = @{ $stream[HEAD] }; for ( 1 .. $#stream ) { @pool = merge(\@pool, $stream[ $_ ]); } # Return first member of pool return shift @pool; } } sub merge { my ($s1, $s2) = @_; return @$s2 if ! @$s1; return @$s1 if ! @$s2; return shift @$s1, merge( @_ ) if $s1->[HEAD] < $s2->[HEAD]; return shift @$s2, merge( @_ ) if $s1->[HEAD] > $s2->[HEAD]; shift @$s1; return shift @$s2, merge( @_ ); }