File Coverage

blib/lib/Perlude/Lazy.pm
Criterion Covered Total %
statement 112 120 93.3
branch 48 56 85.7
condition 10 13 76.9
subroutine 35 36 97.2
pod 0 15 0.0
total 205 240 85.4


line stmt bran cond sub pod time code
1             package Perlude::Lazy;
2 11     11   129208 use strict;
  11         18  
  11         304  
3 11     11   37 use warnings;
  11         13  
  11         208  
4 11     11   97 use 5.10.0;
  11         26  
  11         344  
5 11     11   37 use Carp qw< croak >;
  11         17  
  11         652  
6 11     11   40 use Exporter qw< import >;
  11         14  
  11         545  
7             our $VERSION = '0.0';
8             our @EXPORT = qw<
9              
10             enlist unfold
11             fold
12             takeWhile take drop
13             filter apply
14             now
15             cycle range
16             tuple
17             lines
18             concat
19              
20             >;
21              
22 11     11   36 use Carp;
  11         12  
  11         10809  
23              
24             # End-of-list value: always return itself, with no data
25             {
26             my $NIL;
27             $NIL = sub { $NIL };
28 54     54 0 1392 sub NIL() { $NIL }
29             }
30              
31             # interface with the Perl world
32             sub enlist (&) {
33 68     68 0 5716 my ($i) = @_;
34 68         62 my ( $l, @b );
35             $l = sub {
36 40286 100   40286   54800 if (@_) {
37 44         42 my $n = shift;
38 44 100       137 return ( $l, @b[ 0 .. $n - 1 ] ) if @b >= $n; # there's enough
39 15         22 push @b, my @v = $i->(); # need more
40 15   100     64 push @b, @v = $i->() while @b < $n && @v; # MOAR
41 15 100       423 return ( $l, @b < $n ? @b : @b[ 0 .. $n - 1 ] ); # give it a peek
42             }
43             else {
44 40242 100       39864 return ( $l, shift @b ) if @b; # use the buffer first
45 40224         35776 push @b, $i->(); # obtain more items
46 40224 100       101678 return @b ? ( $l, shift @b ) : NIL;
47             }
48 68         1055 };
49             }
50              
51             sub concat {
52 1     1 0 2 my ($l, @ls)= @_;
53 1         2 my @v;
54             my $r;
55             $r = sub {
56 11     11   14 while ($l) {
57 12         9 ( $l, @v ) = $l->();
58 12 100       32 return ($r,@v) if @v;
59 2         3 $l = shift @ls;
60             }
61 1         3 };
62 1         3 $r
63             }
64              
65             sub unfold (@) {
66 45     45 0 12017 my @array = @_;
67 45 100   185   117 enlist { @array ? shift @array : () };
  185         265  
68             }
69              
70             sub fold ($) {
71 83     83 0 77 my ($l) = @_;
72 83         65 my @v;
73 83 100       129 unless (wantarray) {
74 23 100       46 if ( defined wantarray ) {
75 6         6 my $n = 0;
76 6         400 $n += @v while 1 < ( ( $l, @v ) = $l->() );
77 6         17 return $n;
78             }
79             else {
80             # The real lazy one: when called in scalar context, values are
81             # ignored:
82             # undef while defined ( $l = $l->() );
83             # But producers must be able to handle that
84             # So keep that for later and use the eager implementation for now
85 17         22 undef while 1 < ( ( $l, @v ) = $l->() );
86 17         21 return;
87             }
88             }
89 60         44 my @r;
90 60         77 push @r, @v while 1 < ( ( $l, @v ) = $l->() );
91 60         2277 @r;
92             }
93              
94             # stream consumers (lazy)
95             sub takeWhile (&$) {
96 5     5 0 1662 my ( $cond, $l ) = @_;
97 5         474 my $m;
98             $m = sub {
99 10 100   10   37 1 < ( ( $l, my @v ) = $l->() ) or return ($l);
100 9 100       434 return $cond->() ? ( $m, @v ) : ( sub { ( $l, @v ) } ) for @v;
  0         0  
101 5         33 };
102             }
103              
104             sub filter (&$) {
105 3     3 0 822 my ( $cond, $l ) = @_;
106 3         3 my $m;
107             $m = sub {
108 10006     10006   5076 while (1) {
109 20009 100       31164 1 < ( ( $l, my @v ) = $l->() ) or return ($l);
110 20007   100     23233 $cond->() and return ($m, @v) for @v;
111             }
112 3         15 };
113             }
114              
115             sub take ($$) {
116 71     71 0 4611 my ( $n, $l ) = @_;
117 71         43 my $m;
118             $m = sub {
119 30206 100   30206   31134 $n-- > 0 or return ($l);
120 30174 100       22440 1 < ( ( $l, my @v ) = $l->() ) or return ($l);
121 30135         99584 ( $m, @v );
122             }
123 71         582 }
124              
125             sub drop ($$) {
126 12     12 0 4146 my ( $n, $l ) = @_;
127 12         14 fold take $n, $l;
128 12         15 $l;
129             }
130              
131             sub apply (&$) {
132 2     2 0 45365 my ( $code, $l ) = @_;
133 2         3 my $m;
134             $m = sub {
135 10004 100   10004   7305 1 < ( ( $l, my @v ) = $l->() ) or return $l;
136 10003         10710 ( $m, map $code->(), @v );
137             }
138 2         11 }
139              
140             # stream consumers (exhaustive)
141             sub now (&$) {
142 4     4 0 928 my ( $code, $l ) = @_;
143 4         4 my @b;
144 4         1 while (1) {
145 29 100       66 1 < ( ( $l, my @v ) = $l->() ) or return pop @b;
146 25         31 @b = map $code->(), @v;
147             }
148             }
149              
150             # stream generators
151             sub cycle (@) {
152 2 50   2 0 8 (my @ring = @_) or return NIL;
153 2         4 my $index = -1;
154 10010     10010   8827 enlist { $ring[ ( $index += 1 ) %= @ring ] }
155 2         8 }
156              
157             sub range ($$;$) {
158 14   33 14 0 39 my $begin = shift // croak "range begin undefined";
159 14         10 my $end = shift;
160 14   100     29 my $step = shift // 1;
161              
162 14 50       21 return NIL if $step == 0;
163              
164 14         15 $begin -= $step;
165 14         11 my $l;
166             return $l = defined $end
167             ? $step > 0
168 22 100   22   47 ? sub { ( ( $begin += $step ) <= $end ) ? ( $l, $begin ) : ($l) }
169 8 100   8   26 : sub { ( ( $begin += $step ) >= $end ) ? ( $l, $begin ) : ($l) }
170 14 100   15   388 : sub { ( $l, $begin += $step ) };
  15 100       33  
171             }
172              
173              
174             sub tuple ($$) {
175 6     6 0 2541 my ( $n, $l ) = @_;
176 6 100       290 croak "$n is not a valid parameter for tuple()" if $n <= 0;
177 3         4 my $m;
178             $m = sub {
179 9     9   11 $l = take $n, $l;
180 9         5 my (@r, @v);
181 9         8 push @r, @v while 1 < ( ( $l, @v ) = $l->() );
182 9 100       20 @r ? ( $m, \@r ) : ( $l )
183             }
184 3         13 }
185              
186             sub lines {
187             # private sub that coerce path to handles
188             state $fh_coerce = sub {
189 1     1   2 my $v = shift;
190 1 50       5 return $v if ref $v;
191 0         0 open my ($fh),$v;
192 0         0 $fh;
193 1     1 0 1136 };
194 1         2 my $fh = $fh_coerce->( pop );
195              
196             # only 2 forms accepted for the moment
197             # form 1: lines 'file'
198 1 50 66 4   7 @_ or return enlist { <$fh> // () };
  4         468  
199              
200             # confess if not 2nd form
201 0 0         $_[0] eq 'chomp' or confess 'cannot handle parameters ' , join ',', @_ ;
202              
203             # lines chomp => 'file'
204             enlist {
205 0 0   0     defined (my $v = <$fh>) or return;
206 0           chomp $v;
207 0           $v;
208             }
209              
210 0           }
211              
212             1;
213              
214             =head1 Perlude::Lazy
215              
216             An experimentation of implementing real lazy lists in Perl5.
217             For real world usecases, please use Perlude instead.
218              
219             =head1 SYNOPSIS
220              
221             Haskell prelude miss you when you write perl stuff? Perlude is a port of the
222             most common keywords. Some other keywords where added when there is no haskell
223             equivalent.
224              
225             Example: in haskell you can write
226              
227             nat = [0..]
228             is_even x = ( x `mod` 2 ) == 0
229             evens = filter is_even
230             main = mapM_ print
231             $ take 10
232             $ evens nat
233              
234             in perlude, the same code will be:
235              
236             use Perlude;
237             my $nat = enlist { state $x = 0; $x++ };
238             sub is_even { ($_ % 2) == 0 }
239             sub evens { filter {is_even} shift }
240             traverse {say} take 10, evens $nat
241              
242             =head1 FUNCTIONS
243              
244             all the Perlude documentation is relevent. just replace sub by enlist
245              
246             =cut
247