What we covered today:
- Warmup and Test Suite
- Adobe Office Visit
- YouTeach
- Kylie
- Will
- Singly Linked List
- Yield
- Recursion
We have all seen that you can pass a block (kind of like an anonymous function in Javascript) into methods in Ruby. This allows us to do a bunch more stuff.
Things to remember about blocks...
- They are chunks of code
- You can assign a name to a block
- The code in the block is always enclosed within curly brackets or within a do and an end
When passing in a block, you invoke it by using the yield
statement. An example...
def example
puts "You are in the example method"
yield
puts "You are back in the example method"
yield
end
example do
puts "You are in the block that was yielded"
end
example { puts "You are in the block that was yielded" }
We can obviously pass parameters with the yield statement...
def example
puts "You are in the example method"
yield( 50, "Yay" )
puts "You are back in the example method"
yield( 100, "Woo" )
end
example do |num, proclamation|
puts "You are in the block that was yielded. #{num} and #{proclamation} was passed in."
end
example { |num, proclamation| puts "You are in the block that was yielded. #{num} and #{proclamation} was passed in." }
Finally, we can check whether a block was passed in using the special block_given?
predicate method.
def example
puts "You are in the example method"
yield( 50, "Yay" ) if block_given?
end
example # Just puts "You are in the example method"
example do |num, proclamation|
puts "You are in the block that was yielded. #{num} and #{proclamation} was passed in."
end # Puts multiple things
This is based on the implementation of a SinglyLinkedList found here.
class SinglyLinkedList
include Enumerable
...
end
Enumerable is a mixin, it is a whole chunk of native Ruby code that you can include in any class that you like. The way that it works is that if you provide a working each
method, everything like map
, reject
etc. comes for free. Very useful!
Worth having a look at the Enumerable mixin's documentation. Also, here is a good discussion about Enumerables.
Recursion is a weird concept. More or less, it is when things call themselves. But have a look here, here, here and do this. This is a countdown written without recursion.
def countdown( count )
# THIS
# while count >= 0
# puts count
# count -= 1
# end
# OR THIS
count.downto(0) do |i|
puts i
end
puts "Blast Off!"
end
Things to remember with a recursive method or function...
- Recursive means you need to call itself
- It needs to move towards the base case/end condition
- Recognizes the end condition
And this is it written with recursion. No need for loops!
def countdown_recursive( count )
if count < 0 # Base Case or End Condition
puts "Blast Off!"
else
puts count
countdown_recursive( count - 1 ) # Recursive Call + move towards end condition
end
end
Now, Ruby isn't great at Recursion as it keeps track of exactly where it is in a program. i.e. It will remember that it called the method a million times. Lots of languages are much better but Ruby is heading that way. Have a look at this link. This talks about a thing called Tail Call Optimization, which is more or less a way to get Ruby to not care about where it is in the program or method if the last line in that particular method is recursive. Remember that this is highly experimental, it doesn't come naturally in Ruby.
Another good example of Recursion is to do a Factorial.
Here is the iterative version.
def factorial( num )
product = 1
n.downto( 1 ) do |i|
product *= 1
end
product
end
factorial( 7 ) # => 5040
factorial( 12 ) # => 479001600
And here is the recursive version.
def factorial( num )
return 1 if num <= 1
num * factorial( num - 1 )
end
Another good example of it is the Fibonacci sequence.
Iterative version...
def fibonacci( n )
a = 1
b = 1
while n > 1
a, b = b, a + b # Parallel assignment
n -= 1
end
a
end
1000.times do |i|
puts "fibonacci(#{ i }) = #{ fibonnaci( i ) }."
end
Recursive version...
def fibonacci( n )
if ( n <= 2 )
1
else
fibonacci( n - 1 ) + fibonnaci( n - 2 )
end
end
100.times do |i|
puts "fibonacci(#{ i }) = #{ fibonnaci( i ) }."
end
This is very slow though! Worth having a look at memoization in Ruby if you want to find the most efficient method to get through this. Another faster version...
def fibonacci( n, a = 1, b = 1 )
if n <= 1
a
else
fibonacci( n - 1, b, a + b )
end
end
100.times do |i|
puts "fibonacci(#{ i }) = #{ fibonnaci( i ) }."
end