FAQ on Ruby and ROR by Roseanne Zhang

scjp, advanced, job tips, xml, ant, c/c++/stl, Algorithm, Ruby

The questions and answers here are my selective postings on jGuru, JavaRanch, ChinaJavaWorldProgramFan in order to answer others' questions. After a while of doing this, I realized that a lot of similar questions are being asked again and again.

The purpose of creating this page is to let people in the future have a place to look for answers. People don't need to answer the same question again and again.

Software reusability is extremely important. OO analysis, design, and programming are invented for this purposes. So does the component-based software. Design patterns are discovered for reusing other's design ideas. This page is created for reusing my own answers. Hopefully, this page will make your learning process a little easier.

We all work together to make a difference!

Table of Contents

Ruby


Ruby


Q. Is there a shuffle method for Ruby Array? If not, how to do it?
A: No or yes. There is no shuffle method in standard Ruby.
However, if you do a google search, you will find it in many ways. Here is one works.
class Array
  def shuffle!
    size.downto(1) { |n| push delete_at(rand(n)) }
    self
  end
end

b = [0,1,2,3,4,5]
b.shuffle! 
puts b.join(", ")
To the top

Q. How to calculate big number (such as 1000) non-recursive factorial in Ruby?
A: Easy! Ruby supports big numbers!
See original discussion here
Code here!
n=1000
puts (2..n).inject(1) { |f, n| f * n }
To the top

Q. How to calculate big number (such as 10000) non-recursive fibonacci number in Ruby, recursive way will get stack overflow?
A: There is a beautiful and high speed answer on rubyquiz, however, if n is close to 400, it does not work.
Because it is use hash storage, we don't have exponential time complexity in the recusrsive answer, but stack overflow problem still exists. Here is the beautiful answer from Andrew Johnson
fib_a = Hash.new{ |h, n| n < 2 ? h[n] = n : h[n] = h[n - 1] + h[n - 2] }
Here is my code in non-recursive fashion.
fib_b = Hash.new{ |h, n| 
  if n < 2  
    h[n] = n 
  else
   m = 2  
   while n>=m
    h[m] = h[m-2] + h[m-1]
    m += 1
   end
   h[n]
  end
}

puts fib_b[10000]
It is O(n). All fibonacci number from 1-10000 are stored, if you want fib_b[9000] now, it takes no time at all, O(1). However, there is a problem with this approach, fib_b[11000] will recalculate everything.

Here is an improved version fib_c:

fib_c = Hash.new{ |h, n| 
  if n < 2  
    h[n] = n 
  else
   m = 200  # allowed stack depth, adjust according to your mechine
   while n>m
    h[m] = h[m-2] + h[m-1]
    m += 200
   end
   h[n] = h[n-2] + h[n-1] 
  end
}
fib_c will only recalculate every stack depth, and reuse all other existing results. However, all above algorithm will exausting the memory, will be dead when n is really big. A better O(log(n)) algorithm is provided in the FAQ next.
To the top

  • Q. How to calculate big number (such as 1000000) fibonacci number in O(log(n)) time without using out all the memory, and not causing stack overflow?
    A: This algorithm is provided by MasterRaySoul
    Its correctness can be easily proved by induction. The time complexity is O(log(n)). The original code was in C, and had space complexity of O(n), because the huge nature of fibonacci number, it will run out of memory very fast. Now, I combined the algorithm with Andrew Johnson's memory on demand Hash implemetation, space complexity is also reduced to O(log(n)), made fib[1000000] without stack overflow and no memory problem either. Here is the code. Enjoy!
    fib_d = Hash.new{ |h, n| 
      if (n<2) 
        h[n] = n
      elsif n == 2
        h[n]=1
      else
        a = n/2
        b = a + n%2
        h[n] = h[a]*h[b-1] + h[a+1]*h[b]
      end
    }
    puts fib_d[1000000]
    
    To the top

    Q. How to change string in CamelCase to snake_case?
    A: Easy!
    See original discussion here
    puts "FooBarBaz".split(/(?=[A-Z])/).join('_').downcase # foo_bar_baz
    puts "FooBarBaz".gsub(/(\B[A-Z])/, '_\1').downcase
    puts "FooBarBaz".gsub(/\B[A-Z]/, '_\&').downcase
    
    To the top

    Q. How to put the list of item into a sack to make it full, return true if you can, false if you cannot?
    A: Here is an algorithm with random testing routine.
      $iterations = 0
      def bag(sack, nums)  
        $iterations += 1
        return true if nums.include?(sack)  
        return false if sack < 0 or nums.empty?  
        head, *tail = nums   
        return true if bag(sack - head, tail)  
        if tail.size >= 2 then bag(sack, tail) else return false end  
      end 
      srand
      sack = rand 30
      n = 4
      nums = []
      0.upto sack/2 do |i|
        nums[i] = rand 20
      end
      puts "sack #{sack}, [#{nums.join(',')}]"
      puts bag(sack, nums) 
      puts "iterations = " + $iterations.to_s
    
    To the top

    Q. What is main in Ruby, a method or an Object?
    A: When you start a Ruby program, you are running in an environment of main Object.
    See the following screen result, try yourself, you would know what is going on.
    E:\ex>ruby -e "puts self.kind_of?(Class)"
    false
    E:\ex>ruby -e "puts self.kind_of?(Module)"
    false
    E:\ex>ruby -e "puts self.kind_of?(Object)"
    true
    E:\ex>ruby -e "puts self"
    main
    E:\ex>ruby -e "puts self.class"
    Object
    
    Obviously, main is an Object instance. However, main is also a method of Thread class.
    To the top

    Q. Does Ruby support polymorphism?
    A: There is such need. Ruby is using duck typing to accomplish the job.
    See original discussion here
    Polymorphism is used in compiled OO languages for dynamic/late binding (typing) or binding at runtime. Ruby does everything at runtime.

    Duck-typing is the ruby solution, it does more than just polymorphism. Here is an example in Ruby looks exactly what polymorphism can do.

    Duck-typing does not need ISA relationship, and pay attention to the Radio in the code, and read the comments, please!

    class Animal
      attr_reader :name
      def initialize(name)
        @name= name
      end
      def says
        @name + " says some strange grunty sound"
      end
    end
    
    class Dog < Animal
      def says
        @name + " says Woof!"
      end
    end
    
    class Cat < Animal
      attr_reader :name
      def says
        @name + " says News"
      end
    end
    
    # Attention: Radio is not an Animal
    class Radio 
      attr_reader :name
      def initialize(name)
        @name= name
      end
      def says
        @name + " says News"
      end
    end
    
    animal = Dog.new("Fido")
    puts animal.says
    animal = Cat.new("Socks")
    puts animal.says
    animal = Animal.new("Suzi")
    puts animal.says
    
    # here animal is not an Animal any more!!!
    # It will not compile in C++/Java
    # It is fine, since ruby duck/dynamic typing
    animal = Radio.new("BBC")
    puts animal.says
    
    
    To the top

    Q. What is closure, what are the differences between lamda, block and Proc.new?
    A: Read here to understand closure. Closure in Computer Science
    Here is an example I put together, run it, and read the comments.
    def test_closure_or_not(closure_or_not)
      puts
      puts "before calling closure_or_not"
      result = closure_or_not.call
      puts "after calling closure_or_not"
      puts "result from closure_or_not: #{result}"
      "test_closure_or_not result"
    end
    
    def test_block(&b)
      puts
      puts "before calling block"
      result = b.call
      puts "after calling block"
      puts "result from block: #{result}"
      "test_block result"
    end
    
    puts "======w/o return======"
    # all three are fine without return
    puts "w/o return:" + test_closure_or_not(lambda {"from lambda"})
    puts "w/o return:" + test_closure_or_not(Proc.new {"from Proc.new"})
    puts "w/o return:" + test_block{"from block"}
    
    puts "======w/ return======"
    # Only lambda is fine with return, 
    # lambda is closure, block and Proc.new are not
    puts "w/ return:" + test_closure_or_not(lambda {return "from lambda"})
    puts "w/ return:" + test_closure_or_not(Proc.new {return "from Proc.new"})
    puts "w/ return:" + test_block{return "from block"}
    
    To the top

    Q. A convenient little language translation utility by using google/translation.
    A: It is stealed from herehttp://www.railscn.com/viewtopic.php?p=12892#12892, modified by me.
    It will be a great utility to help me to learn Spanish, and compare the result from Spanish to French, I see they have the same Latin root!!!!
    def usage
      "usage: word [lang2 [lang1]]\n" + 
      "Translate word from lang1 (default en, English) to lang2 (default es, Spanish)\n" +
      "ISO language code: http://www.unicode.org/unicode/onlinedat/languages.html"  
    end
    def translate
      arr = ARGV
      if !arr[0] then puts usage; return end
      arr[1] = "es" unless arr[1]
      arr[2] = "en" unless arr[2]  
      langpair = "#{arr[2]}|#{arr[1]}"    
      response = Net::HTTP.post_form(URI.parse("http://translate.google.com/translate_t"),
                                     {'text' => arr[0],'langpair' => langpair})
      response.body =~ /<div id=result_box dir=ltr>(.*)<\/div>/
      result = $1  
      result = "No #{langpair} translation available for #{arr[0]}" if result.size == 0
      puts result
    end
    translate 
    
    To the top

    last updated: 01-19-2009
    Copyright © 1999 - 2008 Roseanne Zhang, All Rights Reserved
    [JavaChina]