File: Iterator.pm

package info (click to toggle)
libur-perl 0.470%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 7,192 kB
  • sloc: perl: 61,814; javascript: 255; xml: 108; sh: 13; makefile: 9
file content (166 lines) | stat: -rw-r--r-- 3,651 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package UR::Iterator;

use strict;
use warnings;

our $VERSION = "0.47"; # UR $VERSION;

our @CARP_NOT = qw( UR::Object );

# These are not UR Objects.  They're regular blessed references that
# get garbage collected in the regular ways

sub create {
    my $class = shift;
    Carp::croak("$class objects cannot be created via create().  Please see the documentation for more details");
}

sub create_for_list {
    my $class = shift;
    my $items = \@_;

    foreach my $item ( @$items ) {
        unless (defined $item) {
            Carp::croak('undefined items are not allowed in an iterator list');
        }
    }

    my $code = sub {
        shift @$items;
    };
    my $self = bless { _iteration_closure => $code }, __PACKAGE__;
    return $self;
}

sub map($&) {
    my($self, $mapper) = @_;

    my $wrapper = sub {
        local $_ = $self->next;
        defined($_) ? $mapper->() : $_;
    };

    return bless { _iteration_closure => $wrapper }, __PACKAGE__;
}

sub _iteration_closure {
    my $self = shift;
    if (@_) {
        return $self->{_iteration_closure} = shift;
    }
    $self->{_iteration_closure};
}


sub peek {
    my $self = shift;
    unless (exists $self->{peek_value}) {
        $self->{peek_value} = $self->{_iteration_closure}->();
    }
    $self->{peek_value};
}


sub next {
    my $self = shift;
    if (exists $self->{peek_value}) {
        delete $self->{peek_value};
    } else {
        $self->{_iteration_closure}->(@_);
    }
}

sub remaining {
    my $self = shift;
    my @remaining;
    while (defined(my $o = $self->next )) {
        push @remaining, $o;
    }
    @remaining;
}

1;

=pod

=head1 NAME

UR::Iterator - API for iterating through data

=head1 SYNOPSIS

  my $iter = UR::Iterator->create_for_list(1, 2, 3, 4);
  while (my $i = $iter->next) {
    print $i\n";
  }

  my $mapped_iter = $iter->map(sub { $_ + 1 });
  while (my $i = $mapped_iter->next) {
    print "$i\n";
  }

=head1 DESCRIPTION

UR::Iterator instances implement the iterator pattern.  These objects can
be created with either a list of values, or by applying a mapping function
to another iterator.

UR::Iterator instances are normal Perl object references, not UR-based
objects.  They do not live in the Context's object cache, and obey the
normal Perl rules about scoping.

=head1 METHODS

=over 4

=item create_for_list

  $iter = UR::Object::Iterator->create_for_list(@values);

Creates an iterator based on values contained in the given list.  This
constructor will throw an exception if any of the supplied values is
C<undef>.

=item map

  $new_iter = $iter->map(sub { $_ + 1 });

Creates a new iterator based on an existing iterator.  Values returned by this
new iterator are based on the values of the existing iterator after going
through a mapping function.  This new iterator will  be exhausted when the
original iterator is exhausted.

When the mapping function is called, C<$_> is set to the value obtained from
the original iterator.

=item next

  $obj = $iter->next();

Return the next object matching the iterator's rule.  When there are no more
matching objects, it returns undef.

=item peek

  $obj = $iter->peek();

Return the next object matching the iterator's rule without removing it.  The
next call to peek() or next() will return the same object.  Returns undef if
there are no more matching objects.

This is useful to test whether a newly created iterator matched anything.

=item remaining

  @objs = $iter->remaining();

Return a list of all the objects remaining in the iterator.  The list will be
empty if there is no more data.

=back

=head1 SEE ALSO

L<UR::Object::Iterator>

=cut