3 # Copyright(c)2008-2010 Internet Archive. Software license AGPL version 3.
5 # This file is part of BookReader.
7 # BookReader is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # BookReader is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with BookReader. If not, see <http://www.gnu.org/licenses/>.
20 # The BookReader source is hosted at http://github.com/openlibrary/bookreader/
22 from collections import deque
25 class windowed_iterator:
26 """ Wrap an iterator s.t. we can see [window] neighbors
27 in either direction from the current item.
29 Items are stored in a deque of size 2*window + 1, where the latest
30 item is always in the middle position.
32 The supplied clear_callback() is called for items more than
33 [window] steps in the past.
37 # Todo? remove use of None as sentinel, to be able to represent
38 # iterators returning None.
40 def __init__(self, iterator, window, clear_callback=None):
41 self.iterator = iterator
42 # initialize deque with sentinel values
43 self.items = deque((None for i in range(window + 1)),
46 self.clear_callback = clear_callback
50 return str(self.items) + ' window: ' + str(self.window)
52 for item in self.items:
53 if item and self.clear_callback is not None:
54 self.clear_callback(item)
56 def neighbor(self, delta):
57 if abs(delta) > self.window:
58 raise IndexError('Requested delta outside window')
59 while self.window + delta + 1 > len(self.items):
61 self.items.append(self.iterator.next())
64 return self.items[self.window + delta]
65 def neighbors(self, window=None, modtwo=False):
68 if window > self.window:
69 raise IndexError('Requested delta outside window')
70 for i in itertools.chain(range(-window, 0),
71 range(1, window + 1)):
72 if modtwo and i % 2 == 1:
79 if len(self.items) == self.window + 1:
80 # elicit potential StopIteration before clearing/popping
81 nextitem = self.iterator.next()
82 if self.items[0] is not None and self.clear_callback is not None:
83 self.clear_callback(self.items[0])
85 if nextitem is not None:
86 self.items.append(nextitem)
87 return self.items[self.window]
90 if __name__ == '__main__':
92 for i in range(0, 10):
96 c = windowed_iterator(g, 3)
98 for i, item in enumerate(c):
99 print 'item %s: %s' % (i, item)
101 if i in (1, 4, 6, 9):
102 print 'neighbors of item %s: %s' % (i, [n for n in c.neighbors(2)])