-1

My code is supposed to print integers in an array.

odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]    
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return x end }
puts ints

It gives me an error in the 2nd line - in 'block in <main>': unexpected return (LocalJumpError)

When I remove the return, the code works exactly as desired.

To find the mistake in my understanding of blocks, I read related posts post1 and post2. But, I am not able to figure out how exactly are methods and blocks being called and why my approach is incorrect.

Is there some call stack diagram explanation for this ? Any simple explanation ? I am confused because I have only programmed in Java before.

sid smith
  • 533
  • 1
  • 6
  • 18
  • That block is a list comprehension. You can write it simply as `ints = odds_n_ends.select { |x| x.is_a? Integer }` – Diego Basch Sep 20 '14 at 21:41
  • @DiegoBasch - never heard this of list comprehension. Thanks for the suggestion. Can you please tell me why my logic is wrong. How does the ruby language process my logic and throw the error ? If I understand that, then it will be easy for me. – sid smith Sep 20 '14 at 21:47
  • You're not in a method, so there's nothing to return from. See: http://stackoverflow.com/questions/2325471/using-return-in-a-ruby-block – Diego Basch Sep 20 '14 at 21:49
  • @DiegoBasch - Okay. I was thinking that the `return` would make the block return only integers. I thought a block is like a method inside a method. So, i tried to make the inner method/block, return something. – sid smith Sep 20 '14 at 21:57

3 Answers3

2

You generally don't need to worry exactly what blocks are to use them.

In this situation, return will return from the outside scope, e.g. if these lines were in a method, then from that method. It's the same as if you put a return statement inside a loop in Java.

Additional tips:

select is used to create a copied array where only the elements satisfying the condition inside the block are selected:

only_ints = odds_n_ends.select { |x| x.is_a?(Integer) }

You're using it as a loop to "pass back" variables that are integers, in which case you'd do:

only_ints = []
odds_n_ends.each { |x| if x.is_a?(Integer) then only_ints << x end }
Kache
  • 15,647
  • 12
  • 51
  • 79
  • I tried the java equivalent which you suggested and got no error. Am I understanding this correctly ? `public class Stack { public static void main(String[] args) { int r = foo(); System.out.println(r); } public static int foo() { int r = 0; for (int i = 0; i < 5; i++) { return i / 2; } return r; } }` – sid smith Sep 20 '14 at 21:45
  • Just because there are no syntax errors doesn't mean your code is "correct". That code will return from inside the loop every time with a `0 / 2`, i.e. `0`. Why do you have an unreachable return in that method? I find your point unclear. – Kache Sep 20 '14 at 21:48
  • This is what I was trying - put a return statement inside a loop in Java. – sid smith Sep 20 '14 at 21:52
  • In your java code, you're "returning out of method 'foo' from inside a loop". In your ruby code, you're not returning out of anything - there's no method around your code. If that's the entirety of your ruby file, and if you wanted to end the program upon the first integer, then call `exit` – Kache Sep 20 '14 at 21:53
1

If you try to wrap your code in a method then it won't give you an error:

def some_method
  odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
  ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return true end }
  puts ints
end

puts some_method

This code output is true. But wait, where's puts ints??? Ruby didn't reach that. When you put return inside a Proc, then you're returning in the scope of the entire method. In your example, you didn't have any method in which you put your code, so after it encountered 'return', it didn't know where to 'jump to', where to continue to.

Array#select basically works this way: For each element of the array (represented with |x| in your code), it evaluates the block you've just put in and if the block evaluates to true, then that element will be included in the new array. Try removing 'return' from the second line and your code will work:

ints = odds_n_ends.select { |x| if x.is_a?(Integer) then true end }

However, this isn't the most Ruby-ish way, you don't have to tell Ruby to explicitly return true. Blocks (the code between the {} ) are just like methods, with the last expression being the return value of the method. So this will work just as well:

ints = odds_n_ends.select { |x| if x.is_a?(Integer) } # imagine the code between {} is
#a method, just without name like 'def is_a_integer?' with the value of the last expression
#being returned.

Btw, there's a more elegant way to solve your problem:

odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]    
ints = odds_n_ends.grep(Integer)
puts ints

See this link. It basically states:

Returns an array of every element in enum for which Pattern === element.

To understand Pattern === element, simply imagine that Pattern is a set (let's say a set of Integers). Element might or might not be an element of that set (an integer). How to find out? Use ===. If you type in Ruby:

puts Integer === 34

it will evalute to true. If you put:

puts Integer === 'hey'

it will evalute to false.

Hope this helped!

daremkd
  • 8,244
  • 6
  • 40
  • 66
0

In ruby a method always returns it's last statement, so in generall you do not need to return unless you want to return prematurely.

In your case you do not need to return anything, as select will create a new array with just the elements that return true for the given block. As ruby automatically returns it's last statement using

{ |x| x.is_a?(Integer) }

would be sufficient. (Additionally you would want to return true and not x if you think about "return what select expects", but as ruby treats not nil as true it also works...)

Another thing that is important is to understand a key difference of procs (& blocks) and lambdas which is causing your problem:

Using return in a Proc will return the method the proc is used in.

Using return in a Lambdas will return it's value like a method.

Think of procs as code pieces you inject in a method and of lambdas as anonymous methods.

Good and easy to comprehend read: Understanding Ruby Blocks, Procs and Lambdas

When passing blocks to methods you should simply put the value you want to be returned as the last statement, which can also be in an if-else clause and ruby will use the last actually reached statement.

dfherr
  • 1,633
  • 11
  • 24
  • Thanks. I saw your link. In that lin, what does this mean "First, we send the collect! method to an Array with a block of code." Sending a method ? How is it "sending" a method to an array ? Shouldn't it be calling the method of Array class ? – sid smith Sep 20 '14 at 22:22
  • 1
    well a general sending and calling means basically the same thing here. Though ruby implements the "send" method on Object/Kernel level, which basically executes the method name passed to it. So if you want to execute collect! you could do: `Array.collect!` or `Array.send(:collect!)` (the colon states its a symbol not a variable). In ruby `call` references a special method. It can be used on a block and executes the code within. So `x.respond_to(:call)` means the object is a block and you can use `x.call` or `x.send(:call)`. So in ruby send executes a method and call executes a block. – dfherr Sep 21 '14 at 10:40