Wednesday, February 29, 2012

Seven Languages in Seven Weeks - Ruby Day 2 Self-Study

These are my answers to the 2nd self-study section of Seven Languages in Seven Weeks, enjoy.

Find out how to access files with and without code blocks. What is the benefit of the code block?
file = File.open("hello.txt")
line = file.readline
until line.nil? 
  puts line
  line = file.readline
end
file = File.open("hello.txt")
file.each { |line| puts line }
Latter variant is far more concise. Also, using a code block one avoids dealing with the file object's state.

How would you translate a hash to an array? Can you translate arrays to hashes?
Taking a hash, one can convert it into an array of key-value pairs:
hash.each_pair { |key, value| array.push([key, value]) }
Taking an array, one can map each value to its index (which becomes the key):
array.each_with_index { |item, index| hash[index] = item }

Can you iterate through hash?
Yes, using each_pair (see also example above).

You can use Ruby arrays as stacks. What other common data structures do arrays support?
Ruby arrays provide the push and pop stack operations. To use an array as a queue, use push to enqueue and shift to dequeue items. List operations are supported using methods like first, insert, etc.

Print the contents of an array of sixteen numbers, four numbers at the time, using just each.
i = 1
array.each do |x|
  print x, ' '
  puts if i % 4 == 0
  i += 1
end

Now, do the same with each_slice in Enumerable.
array.each_slice(4) { |slice| p slice }

The Tree class was interesting, but it did not allow you to specify a new tree with a clean user interface. Let the initializer accept a nested structure of hashes. You should be able to specify a tree like this: {'grandpa' => {'dad' => {'child 1' => {}, 'child 2' => {} }, 'uncle' => {'child 3' => {}, 'child 4' => {} } } }.
class Tree
  attr_accessor :children, :node_name
  
  def initialize (name, children={}) 
    @node_name = name
    @children = []
    children.each_pair {|key, val| @children << Tree.new(key, val) }
  end
  
  def visit_all (&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  
  def visit (&block)
    block.call self
  end
end
Write a simple grep that will print the lines of a file having any occurrences of a phrase anywhere in that line. You will need to do a simple regular expression match and read lines from a file. (This is surprisingly simple simple in Ruby.) If you want, include line numbers.
def grep (pattern, filename)
  f = File.open(filename)
  f.each {|line| puts "#{f.lineno}: #{line}" if line =~ /#{pattern}/}
end