13

in Ruby it's easy to tell loop to go to next item

(1..10).each do |a|
  next if a.even?
  puts a
end

result =>

1
3   
5
7
9

but what if I need to call next from outside of the loop (e.g.: method)

def my_complex_method(item)
  next if item.even?  # this will obviously fail 
end

(1..10).each do |a|
  my_complex_method(a)
  puts a
end

only solution I found and works is to use throw & catch like in SO question How to break outer cycle in Ruby?

def my_complex_method(item)
  throw(:skip) if item.even? 
end

(1..10).each do |a|
  catch(:skip) do   
    my_complex_method(a)
    puts a
  end
end

My question is: anyone got any more niftier solution to do this ?? or is throw/catch only way to do this ??

Also what If I want to call my_complex_method not only as a part of that loop (=> don't throw :skip) , can I somehow tell my method it's called from a loop ?

Community
  • 1
  • 1
equivalent8
  • 13,754
  • 8
  • 81
  • 109
  • 2
    You're really, *really* over thinking this. Return a value and invoke `next` conditionally based on the returned value. This is an *incredibly* common pattern in software, in virtually every language I've ever used. `throw`/`catch` have no place here. – user229044 Jul 09 '13 at 11:18
  • I agree that for this example it's ridiculous, but I have a case where I need separation of loop and method – equivalent8 Jul 09 '13 at 12:26

3 Answers3

12

You complex method could return a boolean, and then you compare on your loop like this:

def my_complex_method(item)
  true if item.even? 
end

(1..10).each do |a|
  next if my_complex_method(a)
  puts a
end

A simple approach, but different from the try catch one.

UPDATE

As item.even? already return a boolean value, you don't need the true if item.even? part, you can do as follow:

def my_complex_method(item)
  item.even? 
end
MurifoX
  • 14,991
  • 3
  • 36
  • 60
  • I'll go with this answer because it made me realize that `my_complex_method` in my real code can be written different way so this can be achieved (as well as did Meagar comment on my question)... but if anyone ever need to use functionality as I described in my qestion I would go with [Pritis answer](http://stackoverflow.com/a/17546856/473040) rather than with my `throw/catch` solution – equivalent8 Jul 10 '13 at 08:12
1

Enumerator#next and Enumerator#peek will be good option to goo :

def my_complex_method(e)
  return if e.peek.even? 
  p e.peek
end
enum = (1..5).each
enum.size.times do |a|
  my_complex_method(enum)
  enum.next
end

Output

1
3
5
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
0

If all you need is to take actions on only some of values, based on value returned by my_complex_method you could use enumerators wisely:

(1..10).map { |a| [a, my_complex_method(a)] }.each do |a, success|
  puts a if success
end
You could define method accepting block and take some action in this block based on success or failure there: (1..10).each do |a| my_complex_method { |success| next if success } end Thanks to scoping, you are able not to use `catch`/`throw`, and call `next` based on processing status.
samuil
  • 5,001
  • 1
  • 37
  • 44