File Coverage

blib/lib/Iterator/Simple/Lookahead.pm
Criterion Covered Total %
statement 60 60 100.0
branch 16 16 100.0
condition 2 2 100.0
subroutine 13 13 100.0
pod 4 4 100.0
total 95 95 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   793 use 5.008000;
  1         4  
16 1     1   5 use strict;
  1         2  
  1         20  
17 1     1   4 use warnings;
  1         2  
  1         23  
18            
19 1     1   4 use Carp;
  1         2  
  1         58  
20 1     1   7 use Iterator::Simple qw( is_iterator );
  1         1  
  1         93  
21            
22             our $VERSION = '0.09';
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   7 use base 'Iterator::Simple::Iterator', 'Class::Accessor';
  1         2  
  1         817  
77             __PACKAGE__->mk_accessors(
78             '_look_ahead', # list of computed values
79             '_iterators', # list of iterators
80             );
81            
82             sub new {
83 9     9 1 4453 my($class, @items) = @_;
84 9         33 my $self = bless { _look_ahead => [], _iterators => [] }, $class;
85 9 100       36 $self->unget(@items) if @items;
86 9         92 return $self;
87             }
88            
89             use overload (
90 12221     12221   128571 '&{}' => sub { my($self) = @_; return sub { $self->next } },
  12221         49067  
  12221         23828  
91 1         18 '<>' => 'next',
92             '|' => 'filter',
93             fallback => 1,
94 1     1   2417 );
  1         3  
95            
96             #------------------------------------------------------------------------------
97            
98             =head2 peek
99            
100             Retrieves the Nth-element at the head of the stream, but keeps it in the
101             stream to be retrieved by C.
102            
103             Calls the iterator function to compute the items up to the Nth element, returns
104             C if the stream is exhausted before reaching N.
105            
106             As a special case, C retrieves the element at the head of the stream,
107             or C if the stream is empty.
108            
109             =cut
110            
111             #------------------------------------------------------------------------------
112            
113 54819 100   54819   193628 sub _is_iter { ref($_[0]) eq 'CODE' || is_iterator($_[0]) }
114            
115             sub peek {
116 52900     52900 1 10689857 my($self, $n) = @_;
117 52900 100 100     207152 $n ||= 0; croak("negative index $n") if $n < 0;
  52900         106638  
118            
119 52899         71414 while (1) {
120             # return element if already computed
121 107753 100       557555 return $self->_look_ahead->[$n] if $n < @{$self->_look_ahead};
  107753         203743  
122            
123             # empty list of iterators -> end of input
124 54931 100       542604 return unless @{$self->_iterators};
  54931         98829  
125            
126             # get first iterator
127 54854         512971 my $iter = $self->_iterators->[0];
128 54854 100       518122 if ( ! defined $iter ) {
    100          
129 35         37 shift @{$self->_iterators}; # skip undefined values
  35         64  
130             }
131             elsif ( _is_iter($iter) ) {
132 12204         26984 my $value = $iter->();
133 12204 100       63893 if ( defined($value) ) {
134             # allow an iterator to get another
135 12191         17544 unshift @{$self->_iterators}, $value;
  12191         22503  
136             }
137             else {
138 13         19 shift @{$self->_iterators}; # exhausted
  13         25  
139             }
140             }
141             else {
142 42615         209607 push @{$self->_look_ahead}, $iter; # not iterator
  42615         82215  
143 42615         390976 shift @{$self->_iterators};
  42615         78852  
144             }
145             }
146             }
147            
148             #------------------------------------------------------------------------------
149            
150             =head2 next
151            
152             Retrieves the element at the head of the stream and removes it from the stream.
153            
154             Returns C if the stream is empty
155            
156             =cut
157            
158             #------------------------------------------------------------------------------
159            
160             sub next {
161 32665     32665 1 5416945 my($self) = @_;
162 32665         74910 $self->peek; # compute head element
163 32665         547521 return shift @{$self->_look_ahead};
  32665         56180  
164             }
165            
166             #------------------------------------------------------------------------------
167            
168             =head2 unget
169            
170             Pushes back a list of values and/or iterators to the stream, that will be
171             retrieved on the subsequent calls to C.
172            
173             Can be called from within an iterator, to insert values that will be returned
174             before the current call, e.g. calling from the iterator:
175            
176             $stream->unget(1..3); return 4;
177            
178             will result in the values 4,1,2,3 being returned from the stream.
179            
180             =cut
181            
182             #------------------------------------------------------------------------------
183            
184             sub unget {
185 20455     20455 1 10571554 my($self, @items) = @_;
186 20455         36495 unshift @{$self->_iterators}, @items, @{$self->_look_ahead};
  20455         48227  
  20455         232106  
187 20455         193421 @{$self->_look_ahead} = ();
  20455         38732  
188             }
189            
190             #------------------------------------------------------------------------------
191            
192             =head1 EXPORT
193            
194             None.
195            
196             =head1 AUTHOR
197            
198             Paulo Custodio, C<< >>
199            
200             =head1 BUGS and FEEDBACK
201            
202             Please report any bugs or feature requests through the web interface at
203             L.
204            
205             =head1 ACKNOWLEDGEMENTS
206            
207             Inspired in L and L.
208            
209             =head1 COPYRIGHT AND LICENSE
210            
211             Copyright (C) 2013 by Paulo Custodio
212            
213             This library is free software; you can redistribute it and/or modify
214             it under the same terms as Perl itself, either Perl version 5.16.1 or,
215             at your option, any later version of Perl 5 you may have available.
216            
217             =cut
218            
219             1;