· ruby

Ruby: Exiting a 'loop' early

We recently had a problem to solve which at its core required us to iterate through a collection, look up a value for each key and then exit as soon as we’d found a value.

The original solution looped through the collection and then explicitly returned once a value had been found:

def iterative_version
  v = nil
  [1,2,3,4,5].each do |i|
    v = long_running_method i
    return v unless v.nil?
  end
  v
end
def long_running_method(value)
  puts "inside the long running method with #{value}"
  return nil if value > 3
  value
end

Which we run like so:

p "iterative value is #{iterative_version.to_s}"

This prints the following when we run it:

inside the long running method with 1
"iterative value is 1"

I figured there must be a more functional way to solve the problem and I eventually came up with this:

def functional_version
  [1,2,3,4,5].map {|i| long_running_method i }.find { |i| !i.nil? }
end

Which prints the following when we run it:

inside the long running method with 1
inside the long running method with 2
inside the long running method with 3
inside the long running method with 4
inside the long running method with 5
"functional value is 1"

The problem is that collections in Ruby are eager evaluated so we evaluate every single item in the collection before we get the first non nil value.

Luckily the lazylist gem comes to our rescue and allows us to solve the problem in a functional way:

require 'lazylist'
def lazy_version
  lazy_list([1,2,3,4,5]).find { |i| !i.nil? }
end

def lazy_list(values)
  list(long_running_method(values.first)) { lazy_list(values - [values.first]) }
end

Running that gives us this:

inside the long running method with 1
"lazy value is 1"

I’ve never come across a problem where I needed to use a lazy list but finally I have and I think the version which uses it is pretty neat.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket