File Coverage

blib/lib/Iterator/Simple/Lookahead.pm
Criterion Covered Total %
statement 64 64 100.0
branch 16 16 100.0
condition 2 2 100.0
subroutine 14 14 100.0
pod 4 4 100.0
total 100 100 100.0


line stmt bran cond sub pod time code
1             # $Id: Lookahead.pm,v 1.5 2013/07/26 23:25:07 Paulo Exp $
2            
3             package Iterator::Simple::Lookahead;
4            
5             #------------------------------------------------------------------------------
6            
7             =head1 NAME
8            
9             Iterator::Simple::Lookahead - Simple iterator with lookahead and unget
10            
11             =cut
12            
13             #------------------------------------------------------------------------------
14            
15 1     1   1393 use 5.008000;
  1         5  
  1         57  
16 1     1   7 use strict;
  1         3  
  1         53  
17 1     1   23 use warnings;
  1         3  
  1         44  
18            
19 1     1   7 use Carp;
  1         2  
  1         130  
20 1     1   8 use Iterator::Simple qw( is_iterator );
  1         2  
  1         104  
21            
22             our $VERSION = '0.07';
23            
24             #------------------------------------------------------------------------------
25            
26             =head1 SYNOPSIS
27            
28             use Iterator::Simple::Lookahead;
29             my $iter = Iterator::Simple::Lookahead->new( sub {}, @values );
30             my $first = $iter->peek();
31             my $second = $iter->peek(1);
32             my $next = $iter->next; # or $iter->()
33             $iter->unget( sub {}, @values );
34            
35             =head1 DESCRIPTION
36            
37             This module encapsulates an iterator function. An iterator function returns the
38             next element in the stream on each call, and returns C on end of input.
39            
40             The iterator can return a code reference - this new iterator is inserted at the
41             head of the queue.
42            
43             The object allows the user to C the Nth element of the stream without
44             consuming it, or to get it and remove from the stream.
45            
46             A list of items can also be pushed back to the stream by C,
47             to be retrieved in the subsequent C calls. The next item can also be
48             retrieved by calling C<$iter-E()>.
49            
50             The input list to the constructor and to C contains items to be
51             retrieved on each C call, or code references to be called to extract the
52             next item from the stream.
53            
54             Other types of input can be converted to a code reference by C of
55             L.
56            
57             This module is built on top of L
58             and returns iterators that are compatible with the former,
59             i.e. an object of type C
60             can be used as an input to C.
61            
62             =head1 FUNCTIONS
63            
64             =head2 new
65            
66             Creates a new object ready to retrieve elements from the given input list. The
67             list contains either values to be returned in a subsequent C call, or
68             code references to be called to extract the next item from the stream.
69            
70             Other types of input can be converted to a code reference by C of
71             L.
72            
73             =cut
74            
75             #------------------------------------------------------------------------------
76 1     1   14 use base 'Iterator::Simple::Iterator';
  1         2  
  1         635  
77             use Class::XSAccessor {
78 1         14 accessors => [
79             '_look_ahead', # list of computed values
80             '_iterators', # list of iterators
81             ],
82 1     1   775 };
  1         4535  
83            
84             sub new {
85 9     9 1 5880 my($class, @items) = @_;
86 9         73 my $self = bless { _look_ahead => [], _iterators => [] }, $class;
87 9 100       45 $self->unget(@items) if @items;
88 9         51 return $self;
89             }
90            
91             use overload (
92 12221     12221   60300 '&{}' => sub { my($self) = @_; return sub { $self->next } },
  12221         56484  
  12221         25125  
93 1         17 '<>' => 'next',
94             '|' => 'filter',
95             fallback => 1,
96 1     1   503 );
  1         2  
97            
98             #------------------------------------------------------------------------------
99            
100             =head2 peek
101            
102             Retrieves the Nth-element at the head of the stream, but keeps it in the
103             stream to be retrieved by C.
104            
105             Calls the iterator function to compute the items up to the Nth element, returns
106             C if the stream is exhausted before reaching N.
107            
108             As a special case, C retrieves the element at the head of the stream,
109             or C if the stream is empty.
110            
111             =cut
112            
113             #------------------------------------------------------------------------------
114            
115 54819 100   54819   240839 sub _is_iter { ref($_[0]) eq 'CODE' || is_iterator($_[0]) }
116            
117             sub peek {
118 52900     52900 1 10669753 my($self, $n) = @_;
119 52900 100 100     195007 $n ||= 0; croak("negative index $n") if $n < 0;
  52900         111674  
120            
121 52899         51648 while (1) {
122             # return element if already computed
123 107753 100       93832 return $self->_look_ahead->[$n] if $n < @{$self->_look_ahead};
  107753         395606  
124            
125             # empty list of iterators -> end of input
126 54931 100       52768 return unless @{$self->_iterators};
  54931         125760  
127            
128             # get first iterator
129 54854         89646 my $iter = $self->_iterators->[0];
130 54854 100       144428 if ( ! defined $iter ) {
    100          
131 35         195 shift @{$self->_iterators}; # skip undefined values
  35         79  
132             }
133             elsif ( _is_iter($iter) ) {
134 12204         25036 my $value = $iter->();
135 12204 100       58664 if ( defined($value) ) {
136             # allow an iterator to get another
137 12191         10830 unshift @{$self->_iterators}, $value;
  12191         38997  
138             }
139             else {
140 13         18 shift @{$self->_iterators}; # exhausted
  13         118  
141             }
142             }
143             else {
144 42615         241466 push @{$self->_look_ahead}, $iter; # not iterator
  42615         97200  
145 42615         40485 shift @{$self->_iterators};
  42615         93968  
146             }
147             }
148             }
149            
150             #------------------------------------------------------------------------------
151            
152             =head2 next
153            
154             Retrieves the element at the head of the stream and removes it from the stream.
155            
156             Returns C if the stream is empty
157            
158             =cut
159            
160             #------------------------------------------------------------------------------
161            
162             sub next {
163 32665     32665 1 5004635 my($self) = @_;
164 32665         61638 $self->peek; # compute head element
165 32665         32937 return shift @{$self->_look_ahead};
  32665         105164  
166             }
167            
168             #------------------------------------------------------------------------------
169            
170             =head2 unget
171            
172             Pushes back a list of values and/or iterators to the stream, that will be
173             retrieved on the subsequent calls to C.
174            
175             Can be called from within an iterator, to insert values that will be returned
176             before the current call, e.g. calling from the iterator:
177            
178             $stream->unget(1..3); return 4;
179            
180             will result in the values 4,1,2,3 being returned from the stream.
181            
182             =cut
183            
184             #------------------------------------------------------------------------------
185            
186             sub unget {
187 20455     20455 1 9912619 my($self, @items) = @_;
188 20455         28016 unshift @{$self->_iterators}, @items, @{$self->_look_ahead};
  20455         55508  
  20455         50544  
189 20455         22636 @{$self->_look_ahead} = ();
  20455         59575  
190             }
191            
192             #------------------------------------------------------------------------------
193            
194             =head1 EXPORT
195            
196             None.
197            
198             =head1 AUTHOR
199            
200             Paulo Custodio, C<< >>
201            
202             =head1 BUGS and FEEDBACK
203            
204             Please report any bugs or feature requests through the web interface at
205             L.
206            
207             =head1 ACKNOWLEDGEMENTS
208            
209             Inspired in L and L.
210            
211             =head1 COPYRIGHT AND LICENSE
212            
213             Copyright (C) 2013 by Paulo Custodio
214            
215             This library is free software; you can redistribute it and/or modify
216             it under the same terms as Perl itself, either Perl version 5.16.1 or,
217             at your option, any later version of Perl 5 you may have available.
218            
219             =cut
220            
221             1;