Here below a little program in IronRuby that implements 2 classes. There is the main class, called Fiborial (Fibo(nnacci)+(Facto)rial) that implements the Fibonacci and the Factorial algorithms in two ways, one Recursive (using recursion) and the other Imperative (using loops and states). The second class is just an instance class that does the same thing, but its there just to show the difference between static and instance classes, and finally a main function called as module level code.
You can also find 3 more little examples at the bottom. One prints out the Factorial's Series and Fibonacci's Series, the second one just shows a class that mixes both: static and instance members, and finally the third one that uses different return types for the Factorial method to compare the timing and result.
As with the previous posts, you can copy and paste the code below in your favorite IDE/Editor and start playing and learning with it. This little "working" program will teach you some more basics of the Programming Language.
There are some "comments" on the code added just to tell you what are or how are some features called. In case you want to review the theory, you can read my previous post, where I give a definition of each of the concepts mentioned on the code. You can find it here: http://carlosqt.blogspot.com/2011/01/new-series-factorial-and-fibonacci.html
WARNING: the code that you will see below is not following ruby guidelines/idiomatic coding, I did it in purpose to compare ruby's syntax and features side by side with other programming languages... For instance, instead of using a ruby bignum I imported and used System.Numerics.BigInteger instead. Other examples, naming convention and so on, so bear with me!
The Fiborial Program
# Factorial and Fibonacci in IronRuby require "mscorlib" require "System" require "System.Numerics" include System::Collections::Generic include System::Diagnostics include System::Numerics module FiborialRb # Static Class # static classes are not supported in Ruby class StaticFiborial # Static Field @@class_name = "" # no builtin static constructor/initializer support # you can initialize field at this point and even add extra code @@class_name = "Static Constructor" puts @@class_name # Static Method - Factorial Recursive def self.factorial_r(n) if n < 2 BigInteger.One else BigInteger.Multiply(BigInteger.new(n), self.factorial_r(n - 1)) end end # Static Method - Factorial Imperative def self.factorial_i(n) res = BigInteger.One while n > 1 res = BigInteger.Multiply(res, BigInteger.new(n)) n -= 1 end res end # Static Method - Fibonacci Recursive def self.fibonacci_r(n) if n < 2 1 else self.fibonacci_r(n - 1) + self.fibonacci_r(n - 2) end end # Static Method - Fibonacci Imperative def self.fibonacci_i(n) pre = 1 cur = 1 tmp = 0 for i in 2..n tmp = cur + pre pre = cur cur = tmp end cur end # Static Method - Benchmarking Algorithms def self.benchmark_algorithm(algorithm, values) timer = Stopwatch.new i = 0 testValue = 0 facTimeResult = BigInteger.Zero fibTimeResult = 0 # "case/switch" Flow Control Statement case algorithm when 1 puts "\nFactorial Imperative:" # "For in range" Loop Statement for i in 0..values.size - 1 do testValue = values[i] # Taking Time timer.Start facTimeResult = self.factorial_i(testValue) timer.Stop # Getting Time puts " (#{testValue}) = #{timer.Elapsed}" end when 2 puts "\nFactorial Recursive:" # "While" Loop Statement while i < values.size do testValue = values[i] # Taking Time timer.Start facTimeResult = self.factorial_r(testValue) timer.Stop # Getting Time puts " (#{testValue}) = #{timer.Elapsed}" i += 1 end when 3 puts "\nFibonacci Imperative:" # "until" Loop Statement until i == values.size do testValue = values[i] # Taking Time timer.Start fibTimeResult = self.fibonacci_i(testValue) timer.Stop # Getting Time puts " (#{testValue}) = #{timer.Elapsed}" i += 1 end when 4 puts "\nFibonacci Recursive:" # "For each?" Statement values.each do |testValue| # Taking Time timer.Start fibTimeResult = self.fibonacci_r(testValue) timer.Stop # Getting Time puts " (#{testValue}) = #{timer.Elapsed}" end else puts "DONG!" end end end # Instance Class class InstanceFiborial # Instance Field @class_name = "" # Instance Constructor/Initializer def initialize @class_name = "Instance Constructor" puts @class_name end # Instance Method - Factorial Recursive def factorial_r(n) # Calling Static Method StaticFiborial::factorial_r(n) end # Instance Method - Factorial Imperative def factorial_i(n) # Calling Static Method StaticFiborial::factorial_i(n) end # Instance Method - Fibonacci Recursive def fibonacci_r(n) # Calling Static Method StaticFiborial::fibonacci_r(n) end # Instance Method - Fibonacci Imperative def fibonacci_i(n) # Calling Static Method StaticFiborial::fibonacci_i(n) end end # Console Program puts "Static Class" # Calling Static Class and Methods # No instantiation needed. Calling method directly from the class puts "FacImp(5) = #{StaticFiborial::factorial_i(5)}" puts "FacRec(5) = #{StaticFiborial::factorial_r(5)}" puts "FibImp(11)= #{StaticFiborial::fibonacci_i(11)}" puts "FibRec(11)= #{StaticFiborial::fibonacci_r(11)}" puts "\nInstance Class" # Calling Instance Class and Methods # Need to instantiate before using. Calling method from instantiated object ff = InstanceFiborial.new puts "FacImp(5) = #{ff.factorial_i(5)}" puts "FacRec(5) = #{ff.factorial_r(5)}" puts "FibImp(11)= #{ff.fibonacci_i(11)}" puts "FibRec(11)= #{ff.fibonacci_r(11)}" # Create a (Ruby) list of values to test # From 5 to 50 by 5 values = [] for i in (5..50).step(5) values << i end # Benchmarking Fibonacci # 1 = Factorial Imperative StaticFiborial::benchmark_algorithm(1,values) # 2 = Factorial Recursive StaticFiborial::benchmark_algorithm(2,values) # Benchmarking Factorial # 3 = Fibonacci Imperative StaticFiborial::benchmark_algorithm(3,values) # 4 = Fibonacci Recursive StaticFiborial::benchmark_algorithm(4,values) # Stop and exit puts "Press any key to exit..." gets end
And the Output is:
Humm, looks like Fibonnaci's algorithm implemented using recursion is definitively more complex than the others 3 right? I will grab these results for this and each of the upcoming posts to prepare a comparison of time execution between all the programming languages, then we will be able to talk about the algorithm's complexity as well.
Printing the Factorial and Fibonacci Series
require "mscorlib" require "System" require "System.Numerics" include System::Text include System::Numerics module FiborialSeriesRb class Fiborial # Using a StringBuilder as a list of string elements def self.get_factorial_series(n) # Create the String that will hold the list series = StringBuilder.new # We begin by concatenating the number you want to calculate # in the following format: "!# =" series.Append("!") series.Append(n) series.Append(" = ") # We iterate backwards through the elements of the series i = n while i >= 1 # and append it to the list series.Append(i) if i > 1 series.Append(" * ") else series.Append(" = ") end i -= 1 end # Get the result from the Factorial Method # and append it to the end of the list series.Append(self.factorial(n).to_s) # return the list as a string series.to_s end # Using a StringBuilder as a list of string elements def self.get_fibonnaci_series(n) # Create the String that will hold the list series = StringBuilder.new # We begin by concatenating the first 3 values which # are always constant series.Append("0, 1, 1") # Then we calculate the Fibonacci of each element # and add append it to the list for i in 2..n if i < n series.Append(", ") else series.Append(" = ") end series.Append(self.fibonacci(i)) end # return the list as a string series.to_s end def self.factorial(n) if n < 2 BigInteger.One else BigInteger.Multiply(BigInteger.new(n), self.factorial(n - 1)) end end def self.fibonacci(n) if n < 2 1 else self.fibonacci(n - 1) + self.fibonacci(n - 2) end end end # Printing Factorial Series puts "" puts Fiborial::get_factorial_series(5) puts Fiborial::get_factorial_series(7) puts Fiborial::get_factorial_series(9) puts Fiborial::get_factorial_series(11) puts Fiborial::get_factorial_series(40) # Printing Fibonacci Series puts "" puts Fiborial::get_fibonnaci_series(5) puts Fiborial::get_fibonnaci_series(7) puts Fiborial::get_fibonnaci_series(9) puts Fiborial::get_fibonnaci_series(11) puts Fiborial::get_fibonnaci_series(40) # Stop and exit puts "Press any key to exit..." gets end
And the Output is:
Mixing Instance and Static Members in the same Class Instance classes can contain both, instance and static members such as: fields, properties, constructors, methods, etc.
require "mscorlib" # Instance Class class Fiborial # Instance Field @instance_count = 0 # Static Field @@static_count = 0 # Instance Read-Only Getter def get_instance_count() @instance_count end # Static Read-Only Getter def self.get_static_count() @@static_count end # Instance Constructor/Initializer def initialize() @instance_count = 0 puts "\nInstance Constructor #{@instance_count}" end # No Static Constructor/Initializer # You can do a self.initialize() static method, but it will not be called #def self.initialize() # @@static_count = 0 # puts "\nStatic Constructor #{@@static_count}" # Instance Method def factorial(n) @instance_count += 1 puts "\nFactorial(#{n.to_s})" end # Static Method def self.fibonacci(n) @@static_count += 1 puts "\nFibonacci(#{n.to_s})" end # Calling Static Constructor and Methods # No need to instantiate Fiborial::fibonacci(5) # Calling Instance Constructor and Methods # Instance required fib = Fiborial.new fib.factorial(5) Fiborial::fibonacci(15) fib.factorial(5) # Calling Instance Constructor and Methods # for a second object fib2 = Fiborial.new fib2.factorial(5) puts "" # Calling Static Getter puts "Static Count = #{Fiborial::get_static_count}" # Calling Instance Property of object 1 and 2 puts "Instance 1 Count = #{fib.get_instance_count}" puts "Instance 2 Count = #{fib2.get_instance_count}" gets end
And the Output is:
Factorial using int, float, System.Numerics.BigInteger
So, it looks like integers in python can hold big integers, so using (Iron)Ruby int/long or System.Numerics.BigInteger is the same so not much to say here. NOTE: as with the previous scripts you need to manually add a reference to the System.Numerics.dll assembly to your project or SearchPath + require so you can add it to your code.
require "mscorlib" require "System" require "System.Numerics.dll" include System::Numerics include System::Diagnostics # Int/Long Factorial def factorial_int(n) if n == 1 1.to_i else (n * factorial_int(n - 1)).to_i end end # double/float Factorial def factorial_float(n) if n == 1 1.0 else (n * factorial_float(n - 1)).to_f end end # BigInteger Factorial def factorial_bigint(n) if n == 1 BigInteger.One else BigInteger.Multiply(BigInteger.new(n), factorial_bigint(n-1)) end end timer = Stopwatch.new facIntResult = 0 facDblResult = 0.0 facBigResult = BigInteger.Zero i = 0 puts "\nFactorial using Int/Long" # Benchmark Factorial using Int64 for i in (5..50).step(5) timer.Start facIntResult = factorial_int(i) timer.Stop puts " (#{i.to_s}) = #{timer.Elapsed} : #{facIntResult}" end puts "\nFactorial using Float/Double" # Benchmark Factorial using Double for i in (5..50).step(5) timer.Start facDblResult = factorial_float(i) timer.Stop puts " (#{i.to_s}) = #{timer.Elapsed.to_s} : #{facDblResult.to_s}" end puts "\nFactorial using BigInteger" # Benchmark Factorial using BigInteger for i in (5..50).step(5) timer.Start facBigResult = factorial_bigint(i) timer.Stop puts " (#{i.to_s}) = #{timer.Elapsed.to_s} : #{facBigResult.to_s}" end gets
And the Output is:
Yes... I like Python more much more then Ruby !
ReplyDelete