My own Smalltalk has these:
Enumerable
methods for: 'enumerating'
limit: n do: aBlock
"Pass elements of the receiver to aBlock one at a time,
as for #do:, but stop after n elements. n must be a
non-negative SmallInteger. Which elements you get is
for the receiver to decide."
|count|
n < 1 ifTrue: [
n = 0 ifTrue: [^self]ifFalse: [self pvtCountError: n]].
count := 0.
self do: [:each |
aBlock value: each.
(count := count + 1) < n ifFalse: [^self]].
AbstractKeyedCollection
methods for: 'enumerating'
limit: n keysAndValuesDo: aBlock
"Pass keys and corresponding values of the receiver
to aBlock one at a time, as for #keysAndValuesDo:,
but stop after n elements. See #limit:do:."
|count|
n < 1 ifTrue: [
n = 0 ifTrue: [^self]ifFalse: [self pvtCountError: n]].
count := 0.
self keysAndValuesDo: [:key :value |
aBlock value: key value: value.
(count := count + 1) < n ifFalse: [^self]].
Enumerable is the superclass of Collection; it doesn't cover streams but
does
cover 2- and 3-dimensional arrays. AbstractKeyedCollection covers both
dictionaries and sequences. Since they only depend on #do: and
#keysAndValuesDo:
respectively, they should fit nicely into some sort of Trait(s).
I gave a great deal of thought to the names, and decided on "limit" since it
is already in use in a similar sense in things like LimitedOutputStream or
#printStringLimitedTo:. I did consider #do:limitedTo: and
#keysAndValuesDo:limitedTo:
which would be more consistent with #streamContents:limitedTo:, but "heavy"
things
like blocks really work better at the end.