Files
devops/perl/Examples/Chap6/Stream.pm
2025-09-17 16:08:16 +08:00

221 lines
3.7 KiB
Perl

###
### Stream.pm
###
## Chapter 6 section 2
package Stream;
use base Exporter;
@EXPORT_OK = qw(node head tail drop upto upfrom show promise
filter transform merge list_to_stream cutsort
iterate_function cut_loops);
%EXPORT_TAGS = ('all' => \@EXPORT_OK);
sub node {
my ($h, $t) = @_;
[$h, $t];
}
sub head {
my ($s) = @_;
$s->[0];
}
sub tail {
my ($s) = @_;
if (is_promise($s->[1])) {
return $s->[1]->();
}
$s->[1];
}
sub is_promise {
UNIVERSAL::isa($_[0], 'CODE');
}
sub promise (&) { $_[0] }
## Chapter 6 section 2.1
sub upto {
my ($m, $n) = @_;
return if $m > $n;
node($m, promise { upto($m+1, $n) } );
}
sub upfrom {
my ($m) = @_;
node($m, promise { upfrom($m+1) } );
}
## Chapter 6 section 2.2
sub show {
my ($s, $n) = @_;
while ($s && (! defined $n || $n-- > 0)) {
print head($s), $";
$s = tail($s);
}
print $/;
}
## Chapter 6 section 2.2
sub drop {
my $h = head($_[0]);
$_[0] = tail($_[0]);
return $h;
}
## Chapter 6 section 2.2
sub transform (&$) {
my $f = shift;
my $s = shift;
return unless $s;
node($f->(head($s)),
promise { transform($f, tail($s)) });
}
## Chapter 6 section 2.2
sub filter (&$) {
my $f = shift;
my $s = shift;
until (! $s || $f->(head($s))) {
drop($s);
}
return if ! $s;
node(head($s),
promise { filter($f, tail($s)) });
}
## Chapter 6 section 3.1
sub tail {
my ($s) = @_;
if (is_promise($s->[1])) {
$s->[1] = $s->[1]->();
}
$s->[1];
}
## Chapter 6 section 4
sub merge {
my ($S, $T) = @_;
return $T unless $S;
return $S unless $T;
my ($s, $t) = (head($S), head($T));
if ($s > $t) {
node($t, promise {merge( $S, tail($T))});
} elsif ($s < $t) {
node($s, promise {merge(tail($S), $T)});
} else {
node($s, promise {merge(tail($S), tail($T))});
}
}
## Chapter 6 section 5.3
sub list_to_stream {
my $node = pop;
while (@_) {
$node = node(pop, $node);
}
$node;
}
sub insert (\@$$);
sub cutsort {
my ($s, $cmp, $cut, @pending) = @_;
my @emit;
while ($s) {
while (@pending && $cut->($pending[0], head($s))) {
push @emit, shift @pending;
}
if (@emit) {
return list_to_stream(@emit,
promise { cutsort($s, $cmp, $cut, @pending) });
} else {
insert(@pending, head($s), $cmp);
$s = tail($s);
}
}
return list_to_stream(@pending, undef);
}
## Chapter 6 section 5.3
sub insert (\@$$) {
my ($a, $e, $cmp) = @_;
my ($lo, $hi) = (0, scalar(@$a));
while ($lo < $hi) {
my $med = int(($lo + $hi) / 2);
my $d = $cmp->($a->[$med], $e);
if ($d <= 0) {
$lo = $med+1;
} else {
$hi = $med;
}
}
splice(@$a, $lo, 0, $e);
}
## Chapter 6 section 6.1
sub iterate_function {
my ($f, $x) = @_;
my $s;
$s = node($x, promise { &transform($f, $s) });
}
## Chapter 6 section 6.3
sub cut_loops {
my ($tortoise, $hare) = @_;
return unless $tortoise;
# The hare and tortoise start at the same place
$hare = $tortoise unless defined $hare;
# The hare moves two steps every time the tortoise moves one
$hare = tail(tail($hare));
# If the hare and the tortoise are in the same place, cut the loop
return if head($tortoise) == head($hare);
return node(head($tortoise),
promise { cut_loops(tail($tortoise), $hare) });
}
## Chapter 6 section 6.3
sub cut_loops2 {
my ($tortoise, $hare, $n) = @_;
return unless $tortoise;
$hare = $tortoise unless defined $hare;
$hare = tail(tail($hare));
return if head($tortoise) == head($hare)
&& $n++;
return node(head($tortoise),
promise { cut_loops(tail($tortoise), $hare, $n) });
}