Wee Beastie - Scrappy Logo from Rober Frost

Scrappy Academy

Learning Ruby and Rails while keeping it Scrappy

About Ruby Blocks

After learning a bit about Ruby and the basic way that data is stored and manipulated I came across the concept of the Block.

I was enthralled…and bewildered.

The concept seemed simple enough. Blocks are pieces of code, or instructions, that are passed to a function.

Amazing! Passing messages to other messages! This sounds highly advanced. I confess my first thought “Why would I ever want to pass a message to another message”?

This blog entry will begin with the syntax used to pass a Block to a function and finish with a discussion on the usefulness of being able to pass code to a function.

Syntax

So first a bit about Blocks in general. A Block is a bit of code that follows a function call and is contained within a set of braces {} or do / end delimiters. There is a common convention that braces should be used where the Block is only one line and do / end delimiters are used where the code extends over multiple lines. Examples below…

1
some_function(arguments) { puts "Short Code Example" }
1
2
3
4
5
some_function(arguments) do
  puts "This is a style"
  puts "commonly used for code in a block"
  puts "fitting over multiple lines"
end

_Jim Weirich discusses another convention specific to the presence of a return value in his blog entry.

Regardless of how the Block is defined, it is always listed after a message starting on the same line as the message call.

General Syntax

1
2
3
4
5
object.message(arguments) {block}

object.message(arguments) do
  # block
end

Example 1:

1
3.each { puts "I can't stop printing!" }

Console:

1
2
3
I can't stop printing!
I can't stop printing!
I can't stop printing!

Example 2:

1
2
3
4
array = %w[ geography art science ]
array.each do |subject|
  puts "I love #{subject}"
end

Console:

1
2
3
I love art
I love science
I love math

The second example demonstrates how a Block can accept an argument passed to it from the function. In this case it is the subject or value of the array.

Usage

So a block is a piece of logic that is passed with a message. But other than utilizing simple pre-built iterators how can I unleash a Block within my own code? How can I write a function that accepts a Block?

Blocks are implicitly accepted by all functions. Even if the function does not use the block, it is valid syntax to pass it.

However, often, you do want to use the block. To write the function to accommodate the presence of the block use yield. To provide a default behavior if no block is provided it can be switched on with if block_given?

When the function reaches yield the block is invoked. If arguments are to be passed to the block they are listed after yield. ( yield arguments)

1
2
3
4
5
6
7
8
9
def type_of_day
  if block_given?
    puts yield
  else
    puts "I havn't used a block all day, this code sucks"
  end
end

type_of_day { "I'm using a Block just like a real Ruby programmer!" }

Blocks are particulary usefull when you find yourself writing lots of functions containing the same code. If you have five functions with code that differs only by one line…consider using a block. This way, you can condense all of the functions into one well written function that contains a yield statement with the differing code.

Another similar example would be writing a function that contains a bit of logic that might change in the future (think each). If you build a function that iterates through an array and performs a bit of logic on each index you could write a function that accepts a block containing the logic to perform.

The following example shows code that might exist for a program designed to automate yard maintenance. The Yard function has a public interface called maintain_garden that contains several functions needed for garden upkeep.

1
2
3
4
5
6
7
8
9
class Yard
  def maintain_garden
    water_garden
    weed_garden
    yield if block_given?
  end

  # other functions omitted for brevity
end

Utilizing a block the maintain_garden function is dynamically able to provide a way for others to hook in additional behavior. This way, if we decide to add functions like fertilize_garden and harvest_garden we can pass them into the function without having to modify the default behavior.

1
2
3
4
5
6
7
8
9
10
11
12
13
first_yard = Yard.new

### During the winter
first_yard.maintain_garden

### During the spring
first_yard.maintain_garden { fertilize_garden }

### During the fall
first_yard.maintain_garden do
  harvest_garden
  till_garden
end

Conclusion

Block’s can be used to make your functions more versatile allowing you to reduce duplication of code (making your code more DRY). They can also be utilized where logic may change in the future. Instead of having to go back and manipulate the code within a function a block can be used to pass new logic to the existing function.

Palindrome Warm-Up

This week we continued our efforts at Project Euler tackling # 4, Largest Palindrome Product

A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 99.

Find the largest palindrome made from the product of two 3-digit numbers.

This week instead of diving into code head-on we discussed what the exact problem was, We wanted to find the largest palindrome for a product of numbers from 100-999.

We then wrote up our thoughts into a format for the code we thought we should be writing to solve the problem:

products(100..999).select(&:is_palindrome?).max

Doing this allowed us to visualize a possible solution through code. It also made obvious a few small problems we needed to solve before solving the main question.

Working with our code to find a solution:

1. Produce an array of products

We looked through Ruby Doc to find the perfect method to call on an existing array to create a new array of products.

repeated_combination was exactly what we were looking for. This creates a new array of all the combinations of elements in your existing array, without duplicates.

We then used the map method to generate the products of these new number pairs.

Our Resulting Code:

1
2
3
4
5
def products(range)
  # Range can be an array or numbers or a Range. Either way calling `to_a`
  # ensures we have an array to work with
  range.to_a.repeated_combination(2).map{|nums| nums.reduce(:*)}
end

2. Evaluate products to see which products are palindromes

This involved much discussion. At first we discussed manipulating the values as an array.

What if we called reverse on the first three and last three indexes of the array values and if they were equal we had our palindromes?

This could be a solution, but would result in a lot of labor intensive work by the function to manipulate every value several times.

Will, a Scrappy member, brought up changing the array to a string. This way we could just call reverse on all the strings. Then it is simply a matter of checking if the string is the same forwards and backwards.

This seemed like the best idea. So we decided to delegate the is_palindrome? logic to String:

1
2
3
4
5
class Numeric
  def is_palindrome?
    to_s.is_palindrome?
  end
end

3. Define is_palindrome

This problem was pretty easily solved.

As we had already discussed figuring out what values were palindromes, we just needed our strings before we could implement the function.

1
2
3
4
5
class String
  def is_palindrome?
    self == reverse
  end
end

4. Test our functions

We had solved all our mini problems so now it was time to tackle the main question:

Find the largest palindrome made from the product of two 3-digit numbers from 100-999.

We tested our functions in IRB: products(100...1000, 2).select{|n| n.is_palindrome?}.max sure enough returned the correct answer

5. Correct our function to work with any number of arguments

What excitement! We had collectively solved our weekly Project Euler problem, but could we do more?

Yes! Our functions only returned the largest palindrome for a products of 2 numbers. What if we wanted to explore the largest palindrome for the product of 3 numbers? No Problem!

To update our function we created a variable n_elements for the number of arguments to be passed into the repeated_combination method in place of “2”.

Defining a variable for the number of arguments allows you to customize the numbers you’re passing in to create a product. Our new products function looked like this:

1
2
3
def products(range, n_elements = 2)
  range.to_a.repeated_combination(n_elements).map{|nums| nums.reduce(:*)}
end

6. Wrapping Up

So we had a solution. One thing that should be stated, was that we did monkey patch Ruby core. There is a lot of discussion in the community about how this is bad. In this case, we were willing to take the trade off with this simple patch as it followed these rules:

  • Is local only to our project
  • Is generic and can easily be re-used
  • Another implementation would probably not differ at all

As always, Ruby is a language with lots of power. Use it responsibly.

Final solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def products(range, n_elements = 2)
  # Range can be an array or numbers or a Range. Either way calling `to_a`
  # ensures we have an array to work with
  range.to_a.repeated_combination(n_elements).map{|nums| nums.reduce(:*)}
end

class String
  def is_palindrome?
    self == reverse
  end
end

class Numeric
  def is_palindrome?
    to_s.is_palindrome?
  end
end

products(100..999, 2).select(&:is_palindrome?).max

What else happened at Scrappy?

After the warm-up we started exploring Coffee Script. The rest of our meetings this month will be focusing on the Campfire chat client ‘Hubot.’ Hubot uses Coffee Script to interact with users and have some fun.

What is Coffee Script?

Coffee Script is a Ruby-like language that compiles to Javascript.

Why would you use Coffee Script instead of good ole Javascript?

There are several idioms specific to Javascript that can be overlooked after a long day of coding in Rails. Coffee Script can make it easier to switch from back end to front end work without overlooking some of those specific idioms.

Also, CoffeeScript is available by default in Rails. So it’s just one of the other technologies that are available for developer to learn.

We started exploring Hubot in campfire and even made our first update. We cloned the Hubot from Scrappy Academy’s main repo directly onto our machine from GitHub

Next we made a branch on the Scrappy Academy Hubot repo to match the issue we wanted to solve, in this case we created the branch “Fix Cheer Me Up”.

Our goal with “Fix Cheer Me Up” was to tell our Hubot to listen for the phrase “cheer me up” and respond instead of having to ask our Hubot directly to “cheer me up”.

After cloning the repo onto our machine we opened up the script file and then opened cheer.coffee. We located “cheer me up” and updated robot.respond /cheer me up... to robot.hear /cheer me up...

Now we needed to update the main repo with the changes we made on our local machine.

We went to GitHub and created a Pull Request to merge the updates from our “Fix Cheer Me Up” branch to the Hubot “Master” branch.

We checked to make sure all systems were a go and then merged our updates onto Master, success!

Now our hubot takes note anytime “cheer me up” is mentioned in converstaion on our Campfire site instead of having to ask our hubot directly to cheer us up!

This week our goal is to tackle some of the issues with Hubot in the Scrappy Academy repo. What issues are you tackling this week on Hubot?

Fibonacci Warm-Up

Something we started doing just this week, and plan on continuing moving forward, is tackling “warm-up” problems. This is reminiscent of high school math class. These are relatively simple programming problems that are solved via pairing.

This week, we solved via group-pairing is where the entire group participates in solving the problem. One person is designated as the coder. S/he displays her/his laptop on the conference room TV (via an Apple TV). The group then discusses the problem, and codes up a solution together.

Often this leads into side discussions about various programming techniques, tips, tricks, etc. It’s a good time for all. Everyone at different experience levels walks away with something new.

Last week we started tackling Project Euler problems. Up first was Even Fibonacci numbers. This appeared to be a relatively straight forward problem. The first solution that was attempted looked a bit like:

1
2
3
4
5
6
7
8
9
10
11
a = 1
b = 1
sum = 0
while b < 4_000_000
  a = b
  b = a + b
  if b.even?
    sum = sum + b
  end
end
puts sum

However, this produced an incorrect result. Upon further investigation, the bug was in lines 5 and 6. Due to the order of the statements the value of a gets overwritten, before it can be added to b. The evaluation looks a bit like this:

1
2
3
4
a = 1; b = 1    # Before loop
a = 1; b = 2    # 1st pass
a = 2; b = 4    # 2nd pass
a = 4; b = 8    # 3rd pass

So to fix this, the group replaced those two lines of code with:

1
2
3
tmp = b
b = a + b
a = tmp

Now when things get evaluated, it looks like:

1
2
3
4
a = 1; b = 1; tmp = nil    # Before loop
a = 1; b = 2; tmp = 1      # 1st pass
a = 2; b = 3; tmp = 2      # 2nd pass
a = 3; b = 5; tmp = 3      # 3rd pass

This led into a side discussion about some Ruby idioms. Namely, multiple assignment. We were able to replace these three lines of code with one:

1
a, b = b, a+b

This will do exactly as you expect. It helps to understand what Ruby is doing behind the scenes. In essence, it’s evaluating and storing the right-hand-side (rhs as it is commonly seen in error messages). Then it uses the splat to dereference the values and assign them in turn. Think of it as:

1
2
tmp = [b, a+b]
a, b = *tmp

Additionally, we talked about the if and unless one-liners. These are great ways to shorten up the code. Our final solution, took the form:

1
2
3
4
5
6
7
a = b = 1
sum = 0
while b < 4_000_000
  a, b = b, a+b
  sum += b if b.even?
end
puts sum

While this is a good solution. It wasn’t very re-useable. So to make it more re-useable we wrapped it in a method:

1
2
3
4
5
6
7
8
9
def even_fibonnaci_sum(upper_limit)
  a = b = 1
  sum = 0
  while b < upper_limit
    a, b = b, a+b
    sum += b if b.even?
  end
  sum
end

That’s great. Now we can re-use it anywhere, and we are no longer locked into a 4_000_000 cap. However, the Fibonnaci sequence is very common. And this method doesn’t let us re-use that. So at this point we took a step back, and asked: “What is the code that we wish we could write?” The answer looked something like:

1
Fibonnaci.upto(4_000_000).select(&:even?).sum

A nice little one-liner. And it’s very clear what we are doing. In English, this would read: “For Fibonnaci numbers up to 4,000,000. Take the even numbers and sum them.”

Sadly, there is no Fibonnaci number generator, but we can build sequences. In fact the Ruby docs show an example of how to create one Fibonacci Generator.

So we coded one up:

1
2
3
4
5
6
7
8
9
def Fibonnaci
  Enumerator.new do |y|
    a = b = 1
    loop do
      y << a
      a, b = b, a + b
    end
  end
end

Great! We have an Enumerator, but there’s no upto. So we created one:

1
2
3
4
5
6
7
8
9
10
11
12
module Sequentially
  def upto(limit, &block)
    enum = Enumerator.new do |y|
      each do |num|
        break unless num < limit
        y << num
      end
    end

    block ? enum.each(&block) : enum
  end
end

And updated the Fibonnaci generator accordingly:

1
2
3
4
5
def Fibonnaci
  Enumerator.new{ |y|
    # see above for meat
  }.extend Sequentially
end

Sweet! select will work just fine. However, sum doesn’t exist (at least not outside Rails). So, let’s patch that in.

1
2
3
4
5
module Enumerable
  def sum
    reduce(:+)
  end
end

So this final solution is a lot longer. However, it is composed of very re-usable parts that we can continue to use for more Project Euler puzzles. Put that all together and you have the elegant solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
module Enumerable
  def sum
    reduce(:+)
  end
end

module Sequentially
  def upto(limit, &block)
    enum = Enumerator.new do |y|
      each do |num|
        break unless num < limit
        y << num
      end
    end

    block ? enum.each(&block) : enum
  end
end


def Fibonnaci
  Enumerator.new{ |y|
    a = b = 1
    loop do
      y << a
      a, b = b, a + b
    end
  }.extend Sequentially
end

Fibonnaci.upto(4_000_000).select(&:even?).sum