Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
This is a fairly complicated example showing off how cool Ruby's yield and enumerators.
# Below is a complex example of using yielding methods to make a nice interface to use. As you
# can see in RankList#fill!, we don't need to care or check if the location really is empty.
# We also don't need to do the calculation to find the next location to insert at. We can
# iterate through each available location until we reach the end. A cool side effect is that
# we can use any of the enumerable methods on the returned enumerator from #fill_locations
# such as printing the positions of all of the empty spaces by doing:
# p alignment_strategy.fill_locations(@the_grid).to_a
module AlignmentStrategy
class Left
# rank_list: the multi-dimensional array of model locations
# blank_value: the value that represents a location without a model
def self.fill_locations(rank_list, blank_value=nil)
# This magical line will create an enumerator out of this function passing through the two arguments
# if this method doesn't get a block passed to it. It lets you say Left.fill_locations(rank_list).size
# to compute the number of empty locations left in the rank_list.
return to_enum(__callee__, rank_list, blank_value) unless block_given?
# Here we iterate through each rank and each file in each rank
rank_list.each_with_index do |rank, row|
rank.each_with_index do |value, column|
# Is this location blank? Yield its location if it is
if value == blank_value
# These numbers are incremented by one because I decided to number
# the ranks and files starting with one rather than zero.
yield [row + 1, column + 1]
# This function does the reverse of fill_locations, it iterates backwards to make it
# easy to find the first location to remove models from. Not the fanciest of implementations
# but I'm including it for symmetry.
def self.remove_locations(rank_list, removal_value)
return to_enum(__callee__, rank_list, removal_value) unless block_given?
row = rank_list.size - 1
while row >= 0
rank = rank_list[row]
column = rank.size - 1
while column >= 0
value = rank[column + 1]
if value == removal_value
yield [row + 1, column + 1]
column -= 1
row -= 1
class RankList
# This method fills the ranks with the new_value up to number_to_fill times.
# @the_grid is the instance variable containing the multi-dimensional array.
def fill!(new_value, number_to_fill)
# Using our alignment strategy, iterate through locations yielding the rank
# and file number of the next open position.
alignment_strategy.fill_locations(@the_grid) do |rank, file|
# While we still need to fill more locations...
if number_to_fill > 0
@the_grid[rank][file] = new_value
number_to_fill -= 1
# If we're done filling break out of the enumeration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.