Slides
Iterators
Ref. WGR Section 6.3, "Iterators and code blocks"
Loops as Methods
- An Iterator is a method that allows you to loop through all the members of a collection
- Works like "for" or "while" but without any extra language keywords
- It executes a block again and again
- Usually this block is the default block
- this lets you define the block during the method call
- concise and readable (arguably)
times
is on your side
potatoes = nil
3.times do |i|
potatoes = i+1
puts "#{potatoes} potato"
end
puts potatoes + 1
prints
1 potato
2 potato
3 potato
4
implementing times
using until
def times x
i = 0
until i == x
yield i
i += 1
end
x
end
(times
returns the number itself)
to each
his own
["apple", "banana", "cherry"].each do |fruit|
puts "I love #{fruit}!"
end
warning: each returns the collection, not the value
def count_chars a
c = 0
a.each do |s|
c += s.length # this returns c
end # but this returns a
c # so this returns c (again)
end
count_chars ["apple", "banana", "cherry"]
=> 17
implementing each
using until
def each a
i = 0
until i == a.size
yield a[i]
i += 1
end
a
end
the map
is not the territory
["apple", "banana", "cherry"].map do |fruit|
fruit.reverse
end
=> ["elppa", "ananab", "yrrehc"]
implementing map
using each
def map input
a = []
input.each do |item|
a << yield(item)
end
a
end
Other Awesome Iterators
-
select
(aliasfind_all
)- returns all items for which the block returns true(ish)
-
reject
- returns all items for which the block returns false(ish)
-
collect
(aliasmap
)- makes a new array out of whatever the block returns
-
detect
(aliasfind
)- returns a single item, not an array
- returns the first item which makes the block return true(ish)
-
inject
- accumulates (huh?) -- more on this later
Ref. Using Select Etc.
lethal inject
ion
-
inject
is a really fun iterator, but it's really weird - it passes a persistent "accumulator" to each iteration
the return value of the block becomes the next accumulator
-
"inject" is also called "reduce", "fold", and "accumulate" in other languages
inject
example
class Array
def sum
self.inject(0) do |total, current|
total + current
end
end
end
[1,2,3].sum #=> 6
- To help understand this, write out a table with the values of total, current, and the return value for each iteration.
inject
reduced
You can also send inject
(or reduce
) the name of a method only:
class Array
def sum
self.reduce(:+)
end
end
[1,2,3].sum #=> 6
Here we are "reducing" the array by calling +
on all its elements in succession.
more help
- Visualizing iterators with colored block diagrams: http://fablednet.posterous.com/thinking-about-iterators
Outline
- Iterators
- Loops as Methods
- `times` is on your side
- implementing `times` using `until`
- to `each` his own
- warning: each returns the *collection*, not the *value*
- implementing `each` using `until`
- the `map` is not the territory
- implementing `map` using `each`
- Other Awesome Iterators
- lethal `inject`ion
- `inject` example
- `inject` reduced
- more help