File Coverage

blib/lib/HOP/Stream.pm
Criterion Covered Total %
statement 88 99 88.8
branch 24 30 80.0
condition 5 12 41.6
subroutine 29 31 93.5
pod 18 18 100.0
total 164 190 86.3


line stmt bran cond sub pod time code
1             package HOP::Stream;
2              
3 2     2   453025 use warnings;
  2         7  
  2         74  
4 2     2   13 use strict;
  2         4  
  2         78  
5              
6 2     2   12 use base 'Exporter';
  2         9  
  2         3836  
7             our @EXPORT_OK = qw(
8             cutsort
9             drop
10             filter
11             head
12             insert
13             is_node
14             iterator_to_stream
15             list_to_stream
16             append
17             merge
18             node
19             promise
20             show
21             tail
22             transform
23             upto
24             upfrom
25             );
26              
27             our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
28              
29             =head1 NAME
30              
31             HOP::Stream - "Higher Order Perl" streams
32              
33             =head1 VERSION
34              
35             Version 0.03
36              
37             =cut
38              
39             our $VERSION = '0.03';
40              
41             =head1 SYNOPSIS
42              
43             =head1 DESCRIPTION
44              
45             This package is based on the Stream.pm code from the book "Higher Order Perl",
46             by Mark Jason Dominus.
47              
48             A stream is conceptually similar to a linked list. However, we may have an
49             infinite stream. As infinite amounts of data are frequently taxing to the
50             memory of most systems, the tail of the list may be a I. A promise,
51             in this context, is merely a promise that the code will compute the rest of
52             the list if necessary. Thus, the rest of the list does not exist until
53             actually needed.
54              
55             The documentation here is not complete. See "Higher Order Perl" by Mark
56             Dominus for a full explanation. Further, this is B code. Patches
57             and suggestions welcome.
58              
59             =head1 EXPORT
60              
61             The following functions may be exported upon demand. ":all" may be specified
62             if you wish everything exported.
63              
64             =over 4
65              
66             =item * cutsort
67              
68             =item * drop
69              
70             =item * filter
71              
72             =item * head
73              
74             =item * insert
75              
76             =item * iterator_to_stream
77              
78             =item * list_to_stream
79              
80             =item * append
81              
82             =item * merge
83              
84             =item * node
85              
86             =item * promise
87              
88             =item * show
89              
90             =item * tail
91              
92             =item * transform
93              
94             =item * upto
95              
96             =item * upfrom
97              
98             =back
99              
100             =head1 FUNCTIONS
101              
102             =head2 node
103              
104             my $node = node( $head, $tail );
105              
106             Returns a node for a stream.
107              
108             The tail of the node may be a I to compute the actual tail when
109             needed.
110              
111             =cut
112              
113             sub node {
114 170     170 1 112115 my ( $h, $t ) = @_;
115 170         624 bless [ $h, $t ], __PACKAGE__;
116             }
117              
118             ##############################################################################
119              
120             =head2 head
121              
122             my $head = head( $node );
123              
124             This function returns the head of a stream.
125              
126             =cut
127              
128             sub head {
129 233     233 1 1762 my ($s) = @_;
130 233 100       354 return undef unless is_node($s);
131 221         597 $s->[0];
132             }
133              
134             ##############################################################################
135              
136             =head2 tail
137              
138             my $tail = tail( $stream );
139              
140             Returns the I of a stream.
141              
142             =cut
143              
144             sub tail {
145 186     186 1 214 my ($s) = @_;
146 186 100       254 return undef unless is_node($s);
147            
148 174 100       400 if ( is_promise( $s->[1] ) ) {
149 131         218 $s->[1] = $s->[1]->();
150             }
151 174         704 $s->[1];
152             }
153              
154             ##############################################################################
155              
156             =head2 is_node
157              
158             if ( is_node($tail) ) {
159             ...
160             }
161              
162             Returns true if the tail of a node is a node. Generally this function is
163             used internally.
164              
165             =cut
166              
167             sub is_node {
168             # Note that this is *not* bad code. Nodes aren't really objects. They're
169             # merely being blessed to ensure that we can disambiguate them from array
170             # references.
171 422     422 1 1546 UNIVERSAL::isa( $_[0], __PACKAGE__ );
172             }
173              
174             ##############################################################################
175              
176             =head2 is_promise
177              
178             if ( is_promise($tail) ) {
179             ...
180             }
181              
182             Returns true if the tail of a node is a promise. Generally this function is
183             used internally.
184              
185             =cut
186              
187             sub is_promise {
188 174     174 1 535 UNIVERSAL::isa( $_[0], 'CODE' );
189             }
190              
191             ##############################################################################
192              
193             =head2 promise
194              
195             my $promise = promise { ... };
196              
197             A utility function with a code prototype (C<< sub promise(&); >>) allowing one
198             to specify a coderef with curly braces and omit the C keyword.
199              
200             =cut
201              
202 142     142 1 1206 sub promise (&) { $_[0] }
203              
204             ##############################################################################
205              
206             =head2 show
207              
208             show( $stream, [ $number_of_nodes ] );
209              
210             This is a debugging function that will return a text representation of
211             C<$number_of_nodes> of the stream C<$stream>.
212              
213             Omitting the second argument will print all elements of the stream. This is
214             not recommended for infinite streams (duh).
215              
216             The elements of the stream will be separated by the current value of C<$">.
217              
218             =cut
219              
220             sub show {
221 2     2 1 1356 my ( $s, $n ) = @_;
222 2         5 my $show = '';
223 2   66     19 while ( $s && ( !defined $n || $n-- > 0 ) ) {
      33        
224 10         16 $show .= head($s) . $";
225 10         13 $s = tail($s);
226             }
227 2         8 return $show;
228             }
229              
230             ##############################################################################
231              
232             =head2 drop
233              
234             my $head = drop( $stream );
235              
236             This is the C function for streams. It returns the head of the stream
237             and and modifies the stream in-place to be the tail of the stream.
238              
239             =cut
240              
241             sub drop {
242 110     110 1 1236 my $h = head( $_[0] );
243 110         193 $_[0] = tail( $_[0] );
244 110         342 return $h;
245             }
246              
247             ##############################################################################
248              
249             =head2 transform
250              
251             my $new_stream = transform { $_[0] * 2 } $old_stream;
252              
253             This is the C function for streams. It returns a new stream.
254              
255             =cut
256              
257             sub transform (&$) {
258 33     33 1 58 my $f = shift;
259 33         34 my $s = shift;
260 33 50       60 return unless $s;
261 33     27   84 node( $f->( head($s) ), promise { transform ( $f, tail($s) ) } );
  27         44  
262             }
263              
264             ##############################################################################
265              
266             =head2 filter
267              
268             my $new_stream = filter { $_[0] % 2 } $old_stream;
269              
270             This is the C function for streams. It returns a new stream.
271              
272             =cut
273              
274             sub filter (&$) {
275 6     6 1 10 my $f = shift;
276 6         7 my $s = shift;
277 6   66     24 until ( !$s || $f->( head($s) ) ) {
278 6         42 drop($s);
279             }
280 6 50       42 return if !$s;
281 6     5   11 node( head($s), promise { filter ( $f, tail($s) ) } );
  5         15  
282             }
283              
284             ##############################################################################
285              
286             =head2 merge
287              
288             my $merged_stream = merge( $stream1, $stream2 );
289              
290             This function takes two streams assumed to be in sorted order and merges them
291             into a new stream, also in sorted order.
292              
293             =cut
294              
295             sub merge {
296 28     28 1 35 my ( $S, $T ) = @_;
297 28 50       57 return $T unless $S;
298 28 50       43 return $S unless $T;
299 28         39 my ( $s, $t ) = ( head($S), head($T) );
300 28 100       70 if ( $s > $t ) {
    100          
301 11     10   43 node( $t, promise { merge( $S, tail($T) ) } );
  10         18  
302             }
303             elsif ( $s < $t ) {
304 13     12   43 node( $s, promise { merge( tail($S), $T ) } );
  12         20  
305             }
306             else {
307 4     3   13 node( $s, promise { merge( tail($S), tail($T) ) } );
  3         6  
308             }
309             }
310              
311             ##############################################################################
312              
313             =head2 append
314              
315             my $merged_stream = append( $stream1, $stream2 );
316              
317             This function takes a list of streams and attaches them together head-to-tail
318             into a new stream.
319              
320             =cut
321              
322             sub append {
323 13     13 1 27 my (@streams) = @_;
324              
325 13         27 while (@streams) {
326 15         24 my $h = drop( $streams[0] );
327 15 100   12   58 return node( $h, promise { append(@streams) } ) if defined($h);
  12         23  
328 3         9 shift @streams;
329             }
330 1         2 return undef;
331             }
332              
333             ##############################################################################
334              
335             =head2 list_to_stream
336              
337             my $stream = list_to_stream(@list);
338              
339             Converts a list into a stream. The final item of C should be a promise
340             or another stream. Thus, to generate the numbers one through ten, one could
341             do this:
342              
343             my $stream = list_to_stream( 1 .. 9, node(10, undef) );
344             # or
345             my $stream = list_to_stream( 1 .. 9, node(10) );
346              
347             =cut
348              
349             sub list_to_stream {
350 3     3 1 1524 my $node = pop;
351 3 100       7 $node = node($node) unless is_node($node);
352              
353 3         9 while (@_) {
354 19         21 my $item = pop;
355 19         25 $node = node( $item, $node );
356             }
357 3         11 $node;
358             }
359              
360             ##############################################################################
361              
362             =head2 iterator_to_stream
363              
364             my $stream = iterator_to_stream($iterator);
365              
366             Converts an iterator into a stream. An iterator is merely a code reference
367             which, when called, keeps returning elements until there are no more elements,
368             at which point it returns "undef".
369              
370             =cut
371              
372             sub iterator_to_stream {
373 5     5 1 745 my $it = shift;
374 5         38 my $v = $it->();
375 5 100       22 return unless defined $v;
376 4     4   15 node( $v, sub { iterator_to_stream($it) } );
  4         8  
377             }
378              
379             ##############################################################################
380              
381             =head2 upto
382              
383             my $stream = upto($from_num, $to_num);
384              
385             Given two numbers, C<$from_num> and C<$to_num>, returns an iterator which will
386             return all of the numbers between C<$from_num> and C<$to_num>, inclusive.
387              
388             =cut
389              
390             sub upto {
391 21     21 1 766 my ( $m, $n ) = @_;
392 21 100       47 return if $m > $n;
393 16     16   65 node( $m, promise { upto( $m + 1, $n ) } );
  16         31  
394             }
395              
396             ##############################################################################
397              
398             =head2 upfrom
399              
400             my $stream = upfrom($num);
401              
402             Similar to C, this function returns a stream which will generate an
403             infinite list of numbers starting from C<$num>.
404              
405             =cut
406              
407             sub upfrom {
408 46     46 1 2445 my ($m) = @_;
409 46     41   160 node( $m, promise { upfrom( $m + 1 ) } );
  41         82  
410             }
411              
412             sub insert (\@$$);
413              
414             sub cutsort {
415 0     0 1 0 my ( $s, $cmp, $cut, @pending ) = @_;
416 0         0 my @emit;
417              
418 0         0 while ($s) {
419 0   0     0 while ( @pending && $cut->( $pending[0], head($s) ) ) {
420 0         0 push @emit, shift @pending;
421             }
422              
423 0 0       0 if (@emit) {
424             return list_to_stream( @emit,
425 0     0   0 promise { cutsort( $s, $cmp, $cut, @pending ) } );
  0         0  
426             }
427             else {
428 0         0 insert( @pending, head($s), $cmp );
429 0         0 $s = tail($s);
430             }
431             }
432              
433 0         0 return list_to_stream( @pending, undef );
434             }
435              
436             sub insert (\@$$) {
437 1     1 1 647 my ( $a, $e, $cmp ) = @_;
438 1         2 my ( $lo, $hi ) = ( 0, scalar(@$a) );
439 1         5 while ( $lo < $hi ) {
440 2         4 my $med = int( ( $lo + $hi ) / 2 );
441 2         7 my $d = $cmp->( $a->[$med], $e );
442 2 100       9 if ( $d <= 0 ) {
443 1         3 $lo = $med + 1;
444             }
445             else {
446 1         3 $hi = $med;
447             }
448             }
449 1         4 splice( @$a, $lo, 0, $e );
450             }
451              
452             =head1 AUTHOR
453              
454             Mark Dominus, maintained by Curtis "Ovid" Poe, C<< >>
455              
456             =head1 BUGS
457              
458             Please report any bugs or feature requests to
459             C, or through the web interface at
460             L.
461             I will be notified, and then you'll automatically be notified of progress on
462             your bug as I make changes.
463              
464             =head1 ACKNOWLEDGEMENTS
465              
466             Many thanks to Mark Dominus and Elsevier, Inc. for allowing this work to be
467             republished.
468              
469             =head1 COPYRIGHT & LICENSE
470              
471             Code derived from the book "Higher-Order Perl" by Mark Dominus, published by
472             Morgan Kaufmann Publishers, Copyright 2005 by Elsevier Inc.
473              
474             =head1 ABOUT THE SOFTWARE
475              
476             All Software (code listings) presented in the book can be found on the
477             companion website for the book (http://perl.plover.com/hop/) and is
478             subject to the License agreements below.
479              
480             =head1 LATEST VERSION
481              
482             You can download the latest versions of these modules at
483             L. Feel free to fork and make changes.
484              
485             =head1 ELSEVIER SOFTWARE LICENSE AGREEMENT
486              
487             Please read the following agreement carefully before using this Software. This
488             Software is licensed under the terms contained in this Software license
489             agreement ("agreement"). By using this Software product, you, an individual,
490             or entity including employees, agents and representatives ("you" or "your"),
491             acknowledge that you have read this agreement, that you understand it, and
492             that you agree to be bound by the terms and conditions of this agreement.
493             Elsevier inc. ("Elsevier") expressly does not agree to license this Software
494             product to you unless you assent to this agreement. If you do not agree with
495             any of the following terms, do not use the Software.
496              
497             =head1 LIMITED WARRANTY AND LIMITATION OF LIABILITY
498              
499             YOUR USE OF THIS SOFTWARE IS AT YOUR OWN RISK. NEITHER ELSEVIER NOR ITS
500             LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE PRODUCT WILL MEET YOUR
501             REQUIREMENTS OR THAT ITS OPERATION WILL BE UNINTERRUPTED OR ERROR-FREE. WE
502             EXCLUDE AND EXPRESSLY DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES NOT STATED
503             HEREIN, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
504             PARTICULAR PURPOSE. IN ADDITION, NEITHER ELSEVIER NOR ITS LICENSORS MAKE ANY
505             REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS OR IMPLIED, REGARDING THE
506             PERFORMANCE OF YOUR NETWORK OR COMPUTER SYSTEM WHEN USED IN CONJUNCTION WITH
507             THE SOFTWARE PRODUCT. WE SHALL NOT BE LIABLE FOR ANY DAMAGE OR LOSS OF ANY
508             KIND ARISING OUT OF OR RESULTING FROM YOUR POSSESSION OR USE OF THE SOFTWARE
509             PRODUCT CAUSED BY ERRORS OR OMISSIONS, DATA LOSS OR CORRUPTION, ERRORS OR
510             OMISSIONS IN THE PROPRIETARY MATERIAL, REGARDLESS OF WHETHER SUCH LIABILITY IS
511             BASED IN TORT, CONTRACT OR OTHERWISE AND INCLUDING, BUT NOT LIMITED TO,
512             ACTUAL, SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES. IF THE
513             FOREGOING LIMITATION IS HELD TO BE UNENFORCEABLE, OUR MAXIMUM LIABILITY TO YOU
514             SHALL NOT EXCEED THE AMOUNT OF THE PURCHASE PRICE PAID BY YOU FOR THE SOFTWARE
515             PRODUCT. THE REMEDIES AVAILABLE TO YOU AGAINST US AND THE LICENSORS OF
516             MATERIALS INCLUDED IN THE SOFTWARE PRODUCT ARE EXCLUSIVE.
517              
518             YOU UNDERSTAND THAT ELSEVIER, ITS AFFILIATES, LICENSORS, SUPPLIERS AND AGENTS,
519             MAKE NO WARRANTIES, EXPRESSED OR IMPLIED, WITH RESPECT TO THE SOFTWARE
520             PRODUCT, INCLUDING, WITHOUT LIMITATION THE PROPRIETARY MATERIAL, AND
521             SPECIFICALLY DISCLAIM ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A
522             PARTICULAR PURPOSE.
523              
524             IN NO EVENT WILL ELSEVIER, ITS AFFILIATES, LICENSORS, SUPPLIERS OR AGENTS, BE
525             LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, WITHOUT LIMITATION, ANY LOST
526             PROFITS, LOST SAVINGS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING
527             OUT OF YOUR USE OR INABILITY TO USE THE SOFTWARE PRODUCT REGARDLESS OF WHETHER
528             SUCH DAMAGES ARE FORESEEABLE OR WHETHER SUCH DAMAGES ARE DEEMED TO RESULT FROM
529             THE FAILURE OR INADEQUACY OF ANY EXCLUSIVE OR OTHER REMEDY.
530              
531             =head1 SOFTWARE LICENSE AGREEMENT
532              
533             This Software License Agreement is a legal agreement between the Author and
534             any person or legal entity using or accepting any Software governed by this
535             Agreement. The Software is available on the companion website
536             (http://perl.plover.com/hop/) for the Book, Higher-Order Perl, which is
537             published by Morgan Kaufmann Publishers. "The Software" is comprised of all
538             code (fragments and pseudocode) presented in the book.
539              
540             By installing, copying, or otherwise using the Software, you agree to be bound
541             by the terms of this Agreement.
542              
543             The parties agree as follows:
544              
545             =over 4
546              
547             =item 1 Grant of License
548              
549             We grant you a nonexclusive license to use the Software for any purpose,
550             commercial or non-commercial, as long as the following credit is included
551             identifying the original source of the Software: "from Higher-Order Perl by
552             Mark Dominus, published by Morgan Kaufmann Publishers, Copyright 2005 by
553             Elsevier Inc".
554              
555             =item 2 Disclaimer of Warranty.
556              
557             We make no warranties at all. The Software is transferred to you on an "as is"
558             basis. You use the Software at your own peril. You assume all risk of loss for
559             all claims or controversies, now existing or hereafter, arising out of use of
560             the Software. We shall have no liability based on a claim that your use or
561             combination of the Software with products or data not supplied by us infringes
562             any patent, copyright, or proprietary right. All other warranties, expressed
563             or implied, including, without limitation, any warranty of merchantability or
564             fitness for a particular purpose are hereby excluded.
565              
566             =item 3 Limitation of Liability.
567              
568             We will have no liability for special, incidental, or consequential damages
569             even if advised of the possibility of such damages. We will not be liable for
570             any other damages or loss in any way connected with the Software.
571              
572             =back
573              
574             =cut
575              
576             1; # End of HOP::Stream