Subject: | Streams of array refs do not work properly |
Hello Ovid,
HOP-Sream is great, but it fails on the last stream element when the
stream is composed of array refs, because tail [a,b] is b, even if [a,b]
is the last stream element, and not a node.
The attached solution blesses nodes into package, and checks if an
element is a node in head, tail and list_to_stream.
The change to list_to_stream allows the call
$stream = list_to_stream( 1 .. 10 )
to work, which is more intuitive than the originaly recommended
$stream = list_to_stream( 1 .. 9, node(10, undef) );
I have also added an append function to return a stream to concatenate
the input streams, and added test cases to verify the introduced changes.
Please feel free to comment on these changes and maybe incorporate in
them the next HOP::Stream version.
Best Regards,
Paulo Custodio
P.S.
Perl:
This is perl, v5.8.8 built for MSWin32-x86-multi-thread
(with 50 registered patches, see perl -V for more detail)
Copyright 1987-2006, Larry Wall
Binary build 820 [274739] provided by ActiveState http://www.ActiveState.com
Built Jan 23 2007 15:57:46
OS:
Microsoft Windows XP SP2
Subject: | HOP-Stream-patch-0.01-0.01a.txt |
diff -rc HOP-Stream-0.01/lib/HOP/Stream.pm HOP-Stream-0.01a/lib/HOP/Stream.pm
*** HOP-Stream-0.01/lib/HOP/Stream.pm Thu Oct 27 19:12:19 2005
--- HOP-Stream-0.01a/lib/HOP/Stream.pm Sun Sep 23 11:06:56 2007
***************
*** 12,17 ****
--- 12,18 ----
insert
iterator_to_stream
list_to_stream
+ append
merge
node
promise
***************
*** 30,40 ****
=head1 VERSION
! Version 0.01
=cut
! our $VERSION = '0.01';
=head1 SYNOPSIS
--- 31,41 ----
=head1 VERSION
! Version 0.01a
=cut
! our $VERSION = '0.01a';
=head1 SYNOPSIS
***************
*** 75,80 ****
--- 76,83 ----
=item * list_to_stream
+ =item * append
+
=item * merge
=item * node
***************
*** 108,114 ****
sub node {
my ( $h, $t ) = @_;
! [ $h, $t ];
}
##############################################################################
--- 111,117 ----
sub node {
my ( $h, $t ) = @_;
! bless [ $h, $t ], __PACKAGE__;
}
##############################################################################
***************
*** 124,129 ****
--- 127,133 ----
sub head {
my ($s) = @_;
+ return undef unless is_node($s);
$s->[0];
}
***************
*** 139,144 ****
--- 143,150 ----
sub tail {
my ($s) = @_;
+ return undef unless is_node($s);
+
if ( is_promise( $s->[1] ) ) {
$s->[1] = $s->[1]->();
}
***************
*** 147,152 ****
--- 153,175 ----
##############################################################################
+ =head2 is_node
+
+ if ( is_node($tail) ) {
+ ...
+ }
+
+ Returns true if the tail of a node is a node. Generally this function is
+ used internally.
+
+ =cut
+
+ sub is_node {
+ UNIVERSAL::isa( $_[0], __PACKAGE__ );
+ }
+
+ ##############################################################################
+
=head2 is_promise
if ( is_promise($tail) ) {
***************
*** 284,289 ****
--- 307,334 ----
##############################################################################
+ =head2 append
+
+ my $merged_stream = append( $stream1, $stream2 );
+
+ This function takes a list of streams and attaches them together head-to-tail
+ into a new stream.
+
+ =cut
+
+ sub append {
+ my ( @streams ) = @_;
+
+ while ( @streams ) {
+ my $h = drop($streams[0]);
+ return node( $h, promise { append( @streams ) } ) if defined($h);
+ shift @streams;
+ }
+ return undef;
+ }
+
+ ##############################################################################
+
=head2 list_to_stream
my $stream = list_to_stream(@list);
***************
*** 300,305 ****
--- 345,352 ----
sub list_to_stream {
my $node = pop;
+ $node = node($node) unless is_node($node);
+
while (@_) {
my $item = pop;
$node = node( $item, $node );
diff -rc HOP-Stream-0.01/t/10-streams.t HOP-Stream-0.01a/t/10-streams.t
*** HOP-Stream-0.01/t/10-streams.t Thu Oct 27 19:12:19 2005
--- HOP-Stream-0.01a/t/10-streams.t Sun Sep 23 11:05:41 2007
***************
*** 2,9 ****
use warnings;
use strict;
! #use Test::More tests => 21;
! use Test::More 'no_plan';
use lib 'lib/', '../lib/';
--- 2,9 ----
use warnings;
use strict;
! use Test::More tests => 61;
! #use Test::More 'no_plan';
use lib 'lib/', '../lib/';
***************
*** 19,24 ****
--- 19,25 ----
insert
iterator_to_stream
list_to_stream
+ append
merge
node
promise
***************
*** 110,115 ****
--- 111,131 ----
is_deeply \@numbers, [ 2, 4, 6, 8, 10 ],
'... which should return the numbers we expect';
+ # append
+
+ my $stream1 = upto(4, 7);
+ my $stream2 = upto(12, 15);
+ my $stream3 = upto(25, 28);
+ ok $stream = append($stream1, $stream2, $stream3),
+ "append() should return a stream";
+
+ @numbers = ();
+ while ( defined( my $num = drop($stream) ) ) {
+ push @numbers, $num;
+ }
+ is_deeply \@numbers, [ 4..7, 12..15, 25..28 ],
+ '... and the stream should return all of the numbers';
+
# merge
sub scale {
***************
*** 168,173 ****
--- 184,199 ----
}
is_deeply \@numbers, [ 1 .. 10 ], '... and create the numbers one to ten';
+ # list_to_stream, final node computed internally
+
+ ok $list = list_to_stream( 1 .. 10 ),
+ 'list_to_stream() should return a stream';
+ @numbers = ();
+ while ( defined( my $num = drop($list) ) ) {
+ push @numbers, $num;
+ }
+ is_deeply \@numbers, [ 1 .. 10 ], '... and create the numbers one to ten';
+
# insert
my @list = qw/seventeen three one/; # sorted by descending length
***************
*** 176,178 ****
--- 202,246 ----
is_deeply \@list, [qw/seventeen three four one/],
'insert() should be able to insert items according to our sort criteria';
+ #
+ # streams of array refs do not work properly, because tail [a,b] is b, even if [a,b] is
+ # the last stream element, and not a node
+ # Solution: bless nodes, check is_node in head, tail, list_to_stream
+ #
+ $stream = list_to_stream( [A => 1], [B => 2] );
+ is_deeply $stream,
+ bless([ [A => 1],
+ bless([ [B => 2],
+ undef ],
+ 'HOP::Stream')],
+ 'HOP::Stream'), "stream of array refs";
+ is_deeply head($stream), [A => 1], "... head is array ref";
+ is_deeply tail($stream),
+ bless([ [B => 2],
+ undef ],
+ 'HOP::Stream'), "... tail is stream";
+
+ drop($stream);
+ is_deeply $stream,
+ bless([ [B => 2],
+ undef ],
+ 'HOP::Stream'), "stream of array refs, dropped 1";
+ is_deeply head($stream), [B => 2], "... head is array ref";
+ is_deeply tail($stream), undef, "... tail is stream";
+
+ drop($stream);
+ is_deeply $stream, undef, "stream of array refs, dropped 2";
+ is_deeply head($stream), undef, "... head is undef";
+ is_deeply tail($stream), undef, "... tail is undef";
+
+ drop($stream);
+ is_deeply $stream, undef, "stream of array refs, dropped 3";
+ is_deeply head($stream), undef, "... head is undef";
+ is_deeply tail($stream), undef, "... tail is undef";
+
+ #
+ # use a non-stream as stream
+ #
+ $stream = [1, [2, [3]]];
+ is head($stream), undef, "no head of non-stream";
+ is tail($stream), undef, "no head of non-stream";