|
Class: Iterator
Object
|
+--Collection
|
+--Iterator
- Package:
- stx:libbasic2
- Category:
- Collections-Sequenceable
- Version:
- rev:
1.46
date: 2022/10/07 07:58:32
- user: stefan
- file: Iterator.st directory: libbasic2
- module: stx stc-classLibrary: libbasic2
Occasionally you may have a block that when evaluated can be
treated as a collection -- i.e. it takes another block as parameter,
then applies that to a sequence of values.
Or you have an object which implements some enumeration message (eg. allThingsDo:),
and you want to treat this as a collection.
This goodie wraps the block/target into an object -- an iterator -- which is
part of the collection hierarchy, and therefore inherits a variety of
useful collection-related methods.
See the examples to see how useful that is.
News:
merged in the class protocol of Enumerator, which is another goody found
in the open source world. Enumerator does basically the same.
The comment for Enumerator is:
Enumerator wraps another object that provides an enumeration method
with the same semantics as #do:. It is an implementation of the
[external iterator](http://c2.com/cgi/wiki?ExternalIterator) pattern.
To create an `Enumerator`, send the `#on:selector:` message to the
`Enumerator` class. The resulting enumerator responds to all of the
normal collection enumeration messages. The selector must be the name
of a method that provides `do:` semantics for the target object.
```
subclasses := Enumerator on: Collection selector: #allSubclassesDo:.
(subclasses groupedBy: #superclass) inspect.
```
Some enumeration messages return a collection (`collect:`, `select:`,
`reject:`, and `groupedBy:`). By default, the resulting collection
will be an `OrderedCollection` (or a `Dictionary` whose values are
`OrderedCollections` in the case of `groupedBy:`). If you'd like to
use a different collection type (like `Set` or `Array`), you can use
the `#on:selector:species:` method instead:
```
MyCollectionObject>>elements
^Enumerator on: internalElements selector: #do: species: Set
```
[info:]
NAME Iterator
AUTHOR miw@cs.man.ac.uk (Mario Wolczko)
FUNCTION a wrapper for blocks that iterate over collections
ST-VERSION 4.0 4.1
PREREQUISITES
CONFLICTS
DISTRIBUTION world
VERSION 1
DATE 18 Jun 1991
SUMMARY
[organisation:]
Dept. of Computer Science Internet: mario@cs.man.ac.uk
The University uucp: uknet!!man.cs!!mario
Manchester M13 9PL JANET: mario@uk.ac.man.cs
U.K. Tel: +44-61-275 6146 (FAX: 6236)
______the mushroom project___________________________________
copyrightCOPYRIGHT.
The above file is a Manchester Goodie protected by copyright.
These conditions are imposed on the whole Goodie, and on any significant
part of it which is separately transmitted or stored:
* You must ensure that every copy includes this notice, and that
source and author(s) of the material are acknowledged.
* These conditions must be imposed on anyone who receives a copy.
* The material shall not be used for commercial gain without the prior
written consent of the author(s).
Further information on the copyright conditions may be obtained by
sending electronic mail:
To: goodies-lib@cs.man.ac.uk
Subject: copyright
or by writing to The Smalltalk Goodies Library Manager, Dept of
Computer Science, The University, Manchester M13 9PL, UK
(C) Copyright 1992 University of Manchester
For more information about the Manchester Goodies Library (from which
this file was distributed) send e-mail:
To: goodies-lib@cs.man.ac.uk
Subject: help
instance creation
-
on: aYieldingBlock
-
return an iterator which calls the yielding block to generate elements.
Notice that 'aBlock asIterator' is the same as 'Iterator on:aBlock'
-
on: collection msg: oneArgSelector
-
return an iterator which sends oneArgSelector to collection to enumerate it
Usage example(s):
|i|
i := Iterator on:#(1 2 3 4 5 6 7) msg:#reverseDo:.
i do:[:j |
Transcript showCR:j
].
|
-
on: collection selector: oneArgSelector
-
return an iterator which sends oneArgSelector to collection to enumerate it
Usage example(s):
|i|
i := Iterator on:#(1 2 3 4 5 6 7) msg:#reverseDo:.
i do:[:j |
Transcript showCR:j
].
|
-
on: collection selector: oneArgSelector species: aClass
-
return an iterator which sends oneArgSelector to collection to enumerate it
and returns an instance of aClass when elements are collected.
Usage example(s):
|i|
i := Iterator on:#(1 2 3 4 5 6 7 1 2 3 4 5 6 7) selector:#reverseDo: species:OrderedSet.
i collect:[:j | j * 2].
|
accessing
-
identityIndexOf: anElement
-
Answer the identity index of anElement within the receiver.
If the receiver does not contain anElement, answer 0.
-
identityIndexOf: anElement ifAbsent: exceptionBlock
-
Answer the identity index of anElement within the receiver.
If the receiver does not contain anElement, answer the result
of evaluating the exceptionBlock.
-
indexOf: anElement
-
Answer the index of anElement within the receiver. If the receiver does
not contain anElement, answer 0.
-
indexOf: anElement ifAbsent: exceptionBlock
-
Answer the index of anElement within the receiver. If the receiver does
not contain anElement, answer the result of evaluating the exceptionBlock.
adding & removing
-
add: anObject
-
raises an error: Iterators are read-only
-
remove: oldObject ifAbsent: anExceptionBlock
-
Iterators are read-only.
-
removeIdentical: oldObject ifAbsent: anExceptionBlock
-
Iterators are read-only.
converting
-
asOrderedCollection
-
Answer a new instance of OrderedCollection whose elements are the elements of
the receiver. The order in which elements are added depends on the order in
which the receiver enumerates its elements. In the case of unordered collections,
the ordering is not necessarily the same for multiple requests for the conversion.
enumerating
-
collect: aBlock as: aClass
-
Reimplemented here as Iterator does not support #size.
Usage example(s):
(Iterator on:[:whatToDo| 'abcd' do:whatToDo])
collect:[:element | element asUppercase] as:OrderedCollection
(Iterator on:[:whatToDo| 'abcd' do:whatToDo])
collect:[:element | element asUppercase] as:Array
(Iterator on:[:whatToDo| '1234' do:whatToDo])
collect:[:char | char digitValue] as:ByteArray
|
-
detectLast: aBlock ifNone: anExceptionValue
-
evaluate the argument, aBlock for each element in the receiver until
the block returns true; in this case return the element which caused
the true evaluation. The elements are processed in reverse order.
If none of the evaluations returns true, return the value of anExceptionValue
Usage example(s):
(Iterator on:[:whatToDo| #(1 2 3 4) do:whatToDo]) detectLast:[:n | n odd] ifNone:['sorry']
(Iterator on:[:whatToDo| #(2 4 6 8) do:whatToDo]) detectLast:[:n | n odd] ifNone:['sorry']
|
-
do: aBlock
-
evaluate the argument, aBlock for every element in the receiver.
Here, my generator block will iterate and evaluate the block for each
-
findFirst: aBlock ifNone: exceptionalValue
-
Answer the index of the first element of the receiver
for which aBlock evaluates as true.
-
findLast: aBlock
-
Answer the index of the last element of the receiver
for which aBlock evaluates as true.
-
keysAndValuesDo: aBlock
-
Evaluate aBlock with each of the receiver's key/value pairs
(e.g. indexes and elements) as the arguments.
-
reverseDo: aBlock
-
evaluate the argument, aBlock for each element in reverse order.
inspecting
-
inspectorValueStringInListFor: anInspector
( an extension from the stx:libtool package )
-
returns a string to be shown in the inspector's list.
Redefined to avoid calling the iterator
printing & storing
-
displayOn: aGCOrStream
-
what a kludge - Dolphin and Squeak mean: printOn: a stream;
-
printOn: aStream
-
(comment from inherited method)
append a user readable representation of the receiver to aStream.
The text appended is not meant to be read back for reconstruction of
the receiver. Also, this method limits the size of generated string.
private
-
block
-
-
block: aBlock
-
-
species
-
return the type of collection to be returned by collect, select etc.
-
species: aClass
-
set the type of collection to be returned by collect, select etc.
queries
-
isEmpty
-
(comment from inherited method)
return true, if the receiver is empty
-
newSpeciesForCollecting
-
used when doing collect operations.
Redefined because my size is not known
-
size
-
(comment from inherited method)
return the number of elements in the receiver.
This is usually redefined in subclasses for more performance.
an iterator, generating some numbers
|i|
i :=
[:yield |
yield value:0.
1 to:5 do:[:i |
yield value:i
].
yield value:999.
] asIterator.
i do:[:j |
Transcript showCR:j
].
| obviously this is an educational example - the same effect has:
#(0),(1 to:5),#(999) do:#transcribeCR
however, in the iterator example, no actual collection is ever constructed,
whereas the other code creates a temporary one.
an iterator, simulating a collection of 100 random values:
|i|
i := [:yield |
|rnd|
rnd := Random new.
1 to:100 do:[:i |
yield value:(rnd next)
]
] asIterator.
i do:[:j |
j transcribeCR
].
| obviously another educational example - the same can be done as:
(Random next:100) do:#transcribeCR
however, in the iterator example, no actual collection is ever constructed,
whereas the other code creates a temporary one.
here an example, where iterators really perform better (by not creating a real collection);
generate 10Mio random values and count their distribution.
Notice that asBag expects a collection, but there is no materialized collection with
10Mio numbers in it:
|tenMioRandomNumbers counts|
tenMioRandomNumbers :=
[:yield |
1 to:10000000 do:[:i |
yield value:(Random nextIntegerBetween:0 and:10)
]
] asIterator.
counts := tenMioRandomNumbers asBag.
counts inspect
|
an iterator, traversing an ad-hoc tree:
|addToTree tree i|
'build the sorted tree from a list of numbers...'.
addToTree :=
[:tree :newNode |
newNode value < tree value ifTrue:[
tree left isNil ifTrue:[
tree left:newNode
] ifFalse:[
addToTree value:(tree left) value:newNode
]
] ifFalse:[
tree right isNil ifTrue:[
tree right:newNode
] ifFalse:[
addToTree value:(tree right) value:newNode
]
].
].
(Random next:30) do:[:rndNr |
|newNode|
newNode := { left:nil . value:rndNr . right:nil }.
tree isNil ifTrue:[
tree := newNode
] ifFalse:[
addToTree value:tree value:newNode
]
].
'define the iterator...'.
i := [:yield |
|traverse|
traverse := [:node |
node left notNil ifTrue:[
traverse value:node left
].
yield value:node value.
node right notNil ifTrue:[
traverse value:node right
].
].
traverse value:tree.
] asIterator.
'enumerate...'.
i do:[:j |
Transcript showCR:j
].
|
an iterator calling a selector to enumerate elements,
here, simulating the collection of subclasses of a class
|subclasses|
subclasses := Iterator on:Collection selector:#allSubclassesDo:.
(subclasses groupedBy:#superclass) inspect.
|
an iterator, simulating the collection of all classes in the system:
|i|
i := [:yield |
Smalltalk allClassesDo:[:cls |
yield value:cls
]
] asIterator.
i do:[:cls |
Transcript showCR:cls name
].
|
much like above, one that simulates the collection of all methodNames starting with 'a':
|i|
i := [:yield |
Smalltalk allClassesDo:[:cls |
cls methodDictionary keysAndValuesDo:[:nm :mthd |
(nm startsWith:$a) ifTrue:[
yield value:nm
]
]
]
] asIterator.
i do:[:nm |
Transcript showCR:nm
].
|
an iterator, simulating a collection of the lines
in a file:
|i|
i := [:yield |
|s line|
s := 'smalltalk.rc' asFilename readStream.
[s atEnd] whileFalse:[
line := s nextLine.
yield value:line.
].
s close
] asIterator.
i do:[:j |
j printNL
].
| Smalltalk iterators can be used similar to iterators in Python
(however, in Python, iterators are streamlike
and implicitly created whenever a yield is present in a function,
whereas in Smalltalk, they are collection like and explicitly created as such):
|i coll|
i :=
[:yield |
Transcript showCR:'hello1'.
yield value:1.
Transcript showCR:'hello2'.
yield value:2.
Transcript showCR:'hello3'.
yield value:3.
Transcript showCR:'done'.
] asIterator.
coll do:[:each |
Transcript showCR:each
].
|
|