A simple way to understand the return from a block in a ruby

My code should 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 

This gives me an error in the second line - in 'block in <main>': unexpected return (LocalJumpError)

When I remove the return, the code works exactly as you need.

To find an error in my understanding of blocks, I read the related posts post1 and post2 . But I cannot understand how methods and blocks are called and why my approach is wrong.

Is there an explanation of the call stack scheme? Any simple explanation? I got confused because I used to program only in Java.

-one
ruby
Sep 20 '14 at 21:35
source share
3 answers

Usually you do not need to worry about which blocks should use them.

In this situation, return will return from the outer scope, for example. if these lines were in a method, then from this method. This is the same as if you put the return inside a loop in Java.

Additional tips:

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

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

You use it as a loop to “skip” variables that are integers, in which case you will do:

 only_ints = [] odds_n_ends.each { |x| if x.is_a?(Integer) then only_ints << x end } 
+1
Sep 20 '14 at 21:37
source share
— -

If you try to wrap your code in a method, it will not 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 correct. But wait, where does the ints ??? Rubin did not guess. When you return inside Proc, you return to the scope of the entire method. In your example, you had no method in which you put your code, so after it encountered a “return”, it did not know where to “go”, where to continue.

Array # select basically works as follows: for each element of the array (represented by | x | in your code), it computes the block you just entered, and if the block evaluates to true, then this element will be included in the new array. Try removing the “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 is not Ruby-ish itself; you do not need to specify Ruby to explicitly return true. Blocks (code between {}) are similar to methods, with the last expression being the return value of the method. Thus, 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 is 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 says:

Returns an array of each element in the enumeration for which Pattern === element.

To understand the Pattern === element, just imagine that a pattern is a collection (say, a collection of integers). An element may or may not be an element of this set (integer). How to find out? Use ===. If you type in Ruby:

 puts Integer === 34 

it will be checked for true. If you put:

 puts Integer === 'hey' 

it will be false.

Hope this helps!

+1
Sep 21 '14 at 2:07
source share

In ruby, a method always returns the last statement, so in general you do not need to return if you do not want to return prematurely.

In your case, you do not need to return anything, since select will create a new array containing only those elements that return true for the given block. Since ruby ​​automatically returns this last statement using

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

would be enough. (Also, you would like to return true, not x, if you are thinking of "returning what you choose," but since ruby ​​does not consider zero to be true, it also works ...)

Another important thing is to understand the key difference between procs (& blocks) and lambdas that causes your problem:

Using return in Proc will return a method that uses proc.

Using return in Lambdas will return its value as a method.

Think of procs as the code snippets you enter in a method, and lambdas as anonymous methods.

Good and easy to understand: Understanding Ruby, Procs, and Lambdas blocks

When passing blocks to methods, you just have to put the value you want to return as the last statement, which can also be in an if-else condition, and ruby ​​will use the last actual statement.

0
Sep 20 '14 at 21:50
source share



All Articles