Code Blocks

This will get edited and added upon as everything starts to tie together but for now code blocks are found between either do and end or parentheses {}. It is suggested that you use do/end for multi-line blocks and {} for single line. In general {} has precedence over do/end but we won’t worry about that for now.

A simple example of a block is

>>5.times {puts "Hello"}
Hello
Hello
Hello
Hello
Hello

That is fairly self explanatory. We iterate on the times method by the integer 5 which executes our block 5 times.

Here the instance variable @names is being iterated on by the method each. (Instance variables are represented by one ‘@’ prefixed to the name, class variables have two (ie. @@variable where as our block variables have no prefix) Instance variables have a limited scope to the object that is defined as self, the class variables are obviously available for all of the methods and objects within the class and the block variables  have a limited scope that is only available with their block. 

@names.each do |name|
  puts "Hello #{name}!"
end

if the @names object responds to each it is something you can iterate over .. so we greet each person in turn.Each is a method that accepts a block of code then runs that block for every element in a list, and the parts between do and end are such block.

A block is like an anonymous function or lambda. The variable between pipe characters is the parameter for this block.

In this scenario for every item in the list name is bound to that list element and then the expression puts “Hello @{name}!” is run with that name.

inline that would look like @name.each {|name| puts “Hello @{name}!”}

Some common methods that make use of blocks are find, merge, collect, sort and inject.

FIND – allows us to find objects inside a data set

MERGE (only for hashes) ->

>> h1 = { "a" => 111, "b" => 222}
=> {"b"=>222, "a"=>111}
>> h2 = { "b" => 333, "c" => 444}
=> {"c"=>444, "b"=>333}
>> h1.merge(h2)
=> {"c"=>444, "a"=>111, "b"=>333}

You can see that b was defined twice and ruby automatically takes the last definition.
We can use a block to specify which version of “b” we want.

 
#note that this is an optional block and will only get ran if there is a conflict between #the two keys. Remember the key value system for hashes "b" is the key. 
>> h1.merge(h2) {|key, old, new| old }
=> {"c"=>444, "a"=>111, "b"=>222}

If you want to make the merge permanent add an ‘!’ after the method. (ie. h1.merge!(h2))

COLLECT (or MAP does the same thing) – works best with arrays, hashes and ranges.

 
>> array = [1,2,3,4,5] 
=> [1, 2, 3, 4, 5]
>> array.collect {|i| + 1 } 
=> [1, 1, 1, 1, 1]
>> array.collect {|i| i + 1 }
=> [2, 3, 4, 5, 6]
# you can see the first time we tried to use collect because the variable wasn't called it just entered plus one for each instance.

What is happening here is it’s iterating through our array and passes each value up to the variable i, it then does whatever we tell it to in our code block (in this case add one) then puts that value into a new array.

Things to note when using collect:
– number of items in = number of items out (even though it works with hashes and ranges it always returns an array)
– when using puts… the return value is always nil.

SORT — is a comparison and uses the ‘comparison operator’ <=>
value 1 <=> value 2
returns -1 if one thing is less than the other
0 if they are the same
returns 1 if one thing is greater than the other

so we can use this applied to sorting
-1 moves “left”
0 stays
1 moves “right”

>> array = [3,1,1,5,6,2] 
=> [3, 1, 1, 5, 6, 2]
>> array.sort {|v1,v2| v1 <=> v2}
=> [1, 1, 2, 3, 5, 6]

You can also use sort with strings to do alphabetical sorts and other things like that.

INJECT – “accumulator”
– it is standard to refer to this as memo

With collect we had a transformation iterate through a range but with inject we have it accumulate as it goes along and then store it inside memo. For example:

 
>> (1..10).inject {|memo,n| memo + n } 
=> 55

We declared memo to be the accumulator, ‘n’ variable is where each one of the elements will be yielded up so we can use it in our block. When it starts out memo is going to be equal to 1 because it takes the first value as its initializer.

What is actually happening behind the scenes:

memo = 1
memo = memo + 2
memo = memo + 3
memo = memo + 4 …

Another example:

 
#remember the splay operator turns this into an array
>> array = [*1..10]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> product = array.inject(100) {|memo, n| memo * n }
=> 362880000

#to reiterate what ever happens after || is what will be stored in memo for the next iteration

The real power of blocks is shown when dealing with things more complicated than lists. you can handle setup, teardown and errors. The example from below is a snipet from a Greeting class where we are planning to say hi and say bye to certain people depending on the methods they respond to .

# Say bye to everybody
def say_bye
  if @names.nil?
    puts "..."
  elsif @names.respond_to?("join")
    # Join the list elements with commas
    puts "Goodbye #{@names.join(", ")}.  Come back soon!"
  else
    puts "Goodbye #{@names}.  Come back soon!"
  end
end

Here say_bye doesn’t sue each… it checks to see if @names responds to the join method and if so, uses it. Otherwise just prints out the variable as a string. The moethod of not caring about the actual type of variable just relying on what methods it supports is known as “duck typing” (if it walks like a duck and quacks like a duck). The benefit being that if someone comes up with a new kind of list class as long as it implements the join method with the same semantics as other lists everything will work as planned.

But we will come back and cover the more complicated use cases later for now just know what’s happening in the blocks we have made.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s