My Own Find Each
October 16, 2016
One of my current assignments from Bloc is implementing my very own find_each
function. One of the beauties of Ruby is the ease at which you can iterate over collections.
some_array = %w{apple banana corn dog ear}
some.array.each {|organic_thing| puts "i have an #{organic_thing}"}
That example will output that line once for each item in the collection. Very easy, easy to read, without a ton of extraneous variables or worrying about incrementing and limits as you would in a traditional for
loop.
To understand what the heck I am supposed to do here, I need to venture into the world of Blocks and Yield. I happen to think this is some of the cleverest Ruby, and thus is a bit tricky to conceptualize because Ruby can veer into koan and haiku territory, where very little code is doing a whole lot of stuff.
GREAT Starter Site on the Topic
I cannot recommend this page enough: http://mixandgo.com/blog/mastering-ruby-blocks-in-less-than-5-minutes. It is a great overview of block
and yield
in Ruby.
Block
A block
is generally a method that you pass into another method. They are very common in Ruby, with the familiar syntax of:
anArray.map { |el| puts "this is #{el}" }
# or
someObjectList.each do |obj|
puts obj.name
end
The blocks here are what is between the curlies {} and do/end pairs. We declare a temp variable representing the current element of the collection by putting it between pipes || (called the 'block parameter') and then run the code inside the do/end or curlies. Here, we had the code output something regarding the current element to console, but we could just as well output something random.
There is nothing to a block! You've been using it all along. It is just some code you pass in to a do-end or {}.
&Block
If you want to pass in a method to Ruby as an argument, you can! Why not? The &Block
syntax is pretty common in Ruby, so it is good for me to really get my head around this.
We use &Block
to pass a reference to a block into the code. Here is an example I made from tooling around in irb.
2.3.0 :009 > def textmagic(word, &block)
2.3.0 :010?> block.call(word)
2.3.0 :011?> end
=> :textmagic
2.3.0 :012 > textmagic("magic"){|w| puts "w"}
w
=> nil
So I have a block in my arguments. I can then reference said block inside my method, and even pass in a local variable to the block parameter. Note that I call the block with block.call
- pretty straightforward.
Yield
What yield
does in Ruby is identical to writing block.call
! Just some syntactic sugar, sweet thing. It runs the block passed inside the method. In other words, it specifies where in the method the additional code you are inserting via block is going to go.
With do/end and {}, we are running the code between the magic words or curlies. In a typical use case, we are iterating over a collection and applying the block to each element. However, with a method we write, we might need a more nuanced application of the block. Yield
allows for a custom placement of the code block within the method, with optional arguments passed to yield(arg1, arg2...)
then as before becoming available as block parameters.
Conclusion
Blocks and Yields have taken me a while to wrap my head around, despite, in hindsight, realizing that anonymous functions in JavaScript, which I use all the time as callbacks and whatnot, are blocks. The syntax with Ruby as usual is concise which presents an early learning curve 'what am I even looking at' reaction that later flowers into a resentment at other languages for making me type so much or get buried in parenthesis and curly piles.