Repository navigation
Expand file tree
/
Copy pathiterator.py
More file actions
73 lines (53 loc) · 2.24 KB
/
Copy pathiterator.py
File metadata and controls
73 lines (53 loc) · 2.24 KB
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
"""
Iterator — walk a collection without exposing how it is stored
A `Playlist` keeps its songs privately and hands out a
`PlaylistIterator` that exposes only `has_next` and `next`. The caller
walks the songs through that cursor and never touches the underlying
list — swap the storage and the traversal code is unchanged.
Compare with the procedural Python version, which reaches straight
into the structure with an index:
i = 0
while i < len(playlist.songs):
play(playlist.songs[i])
i += 1
POOP forbids `while`, `len`, and `[]` indexing. The iterator object
holds the cursor; a `while_true` block drives it by sending
`has_next`/`next`, so the loop variable lives inside the iterator, not
the caller.
Smalltalk:
PlaylistIterator>>hasNext ^position < songs size
PlaylistIterator>>next
| song | song := songs at: position.
position := position + 1. ^song
[iterator hasNext] whileTrue: [Transcript showCr: iterator next]
The pattern is about a *custom* cursor over storage a caller must not
see. POOP's own iterators answer the same two messages, so the same
loop drives `["a", "b"].iter()` directly:
it = ["Imagine", "Hey Jude"].iter()
(lambda: it.has_next()).while_true(lambda: it.next().print())
Write `PlaylistIterator` when the traversal is yours to define — a
tree walk, a filtered view, a paged fetch — not to re-implement `iter`.
"""
class Playlist:
def __init__(self, songs):
self._songs = songs
def iterator(self):
return PlaylistIterator(self._songs)
class PlaylistIterator:
def __init__(self, songs):
self._songs = songs
self._position = 0
def has_next(self):
return self._position < self._songs.len()
def next(self):
song = self._songs.at(self._position)
self._position = self._position + 1
return song
playlist = Playlist(["Imagine", "Hey Jude", "Yesterday"])
cursor = playlist.iterator()
(lambda: cursor.has_next()).while_true(lambda: cursor.next().print())
# The same loop, driven by a built-in iterator: `iter()` answers the very
# protocol `PlaylistIterator` implements by hand.
"---".print()
songs = ["Come Together", "Something"].iter()
(lambda: songs.has_next()).while_true(lambda: songs.next().print())