sawa
sawa

Reputation: 168081

Recursive routine in a method

Often, I have a recursive routine within a method that is only called by that method or within the recursive routine itself:

def foo
  ...
  bar
  ...
end

def bar
  ...
  bar
  ...
end

But since bar is not used anywhere else, I do not want to define it as a method but somehow put it inside the method that calls it like this:

def foo
  ...
  bar {# some way to mark the recursive routine
    ...
    bar # some way to call the recursive routine
    ...
  }
  ...
end

Is this possible?

Upvotes: 1

Views: 200

Answers (3)

Marc-André Lafortune
Marc-André Lafortune

Reputation: 79562

Easy with a lambda/proc:

def foo(n)
  fact = lambda do |i|
    i.zero? ? 1 : i * fact.call(i-1)
  end
  fact.call(n)
end

foo(4) # => 24

You can also use a protected or private method.

If performance is a real concern, creating an object (the lambda) each time will be slower, and calling a lambda is also slower, closure and all. My fruity gem gives me a 3.3x slowdown on this trivial example; the penalty should be much less for cases that actually do something more involved. Just be sure that performance really is an issue; you know what they say about premature optimization...

Upvotes: 4

dbenhur
dbenhur

Reputation: 20408

@coreyward 's answer is good, but there's another trick you could use to limit the scope of the recursive function using lambda.

def foo
  ...
  bar = lambda do |arg|
    ...
    bar.call(...)
    ...
  end

  bar.call(...)
  ...
end

Since bar is lexically scoped within foo, no other method can see it or call it, but the lambda which bar refers to can see bar since it closes over the lexical scope where it's defined.

Upvotes: 0

coreyward
coreyward

Reputation: 80041

Sounds like you're trying to do functional programming in an object-oriented language. You might be better served by creating a small class with a single responsibility:

class Routiner
  def foo(*args)
    # occasionally...
    do_work(some_data)
  end
protected
  def do_work(data)
    # ...work work work
    do_work(more_data) if some_condition
  end
end

Routiner.new.foo('bar', 'baz', 'bat')

Upvotes: 2

Related Questions