Learning Ruby

Just recently I started learning a new programming language called Ruby. The first language that I ever learned way back in college is Java so I can't help but compare the two languages. Learning a new language for me is apparently not very difficult since I have a strong foundation in programming logic theoretically as well as based on experience. I was quite amazed on how Ruby has made coding fast, easy, and simple with its intuitive, shorter syntax, and also available online sources (http://ruby-doc.com/docs/ProgrammingRuby/) will help you learn the language quickly.

So, I started reading the first five sections of the documentation and I was too eager to put my hands on it and write some codes. By the way, installing the compiler on Windows is no sweat and you can download it on http://rubyonrails.org/download. The installer automatically appends C:\Ruby200\bin to the system path so you can run the code right away on the command prompt. You just have to create a file with .rb extension and execute it as a command.

I kind of created a challenge for myself by writing a simple calculator program that follows the "MDAS" rule as we've learned in Math. Yes, it's sort of a scientific calculator that only knows the four arithmetic operators. I will also try my best to explain each important segment of the code. As a background, I've chosen this problem because I was never able to write the code before in Java without knowledge of advanced topics. I hope I'm making sense. But in Ruby, I admit I only have basic knowledge and I was able to do it using my knowledge of Arrays and Regular Expressions. To do this, you must have an input in infix notation without using parentheses. Again, this is very basic to start off. For example, we have the expression "5 + 4 * 2 - 6". The expression is converted into a reverse Polish notation or post-fix notation, and the new expression "5 4 2 * + 6 -" is evaluated using the following algorithm:

Infix to Post-fix notation:
1.) Create two arrays: OPN and OPR
2.) Iterate through each character of the infix expression; if the character is a digit, push it into OPN; if the character is one of the arithmetic operators, push it into OPR.
3.) Create another array: NEW
4.) Loop while OPN and OPR are not empty; if NEW contains less than two elements, dequeue the first two elements of OPN and push them into NEW
5.) While in the loop, dequeue the first two elements of OPR, namely OP1 and OP2; if OP2 is null or if OP1 preceeds OP2 (you know, the rule), push OP1 and dequeue the first element of OPN and push it into NEW if OPN is not empty; if OP2 is not null, return OP2 in front of OPR; otherwise, dequeue the first element of OPN and push it and OP2 into NEW and return OP1 in front of OPR.
6.) Lastly, since the number of elements that OPN and OPR contain differs by 1, you need to push the last element of OPR in order to end the loop

Post-fix evaluation:
1.) Create an array: OPN
2.) Iterate through each character of the post-fix expression; if the character is a digit, push it into OPN; otherwise, if OPN contains two or more elements, pop the first two elements of OPN as OP2 for the first pop and OP1 for the second pop; calculate the result and push it back into OPN.
3.) At the end of the loop, the only element left in OPN is the overall result

This came from the top of my head and I'm sure there are better solutions out there. But this is just to show how unique Arrays are in Ruby as they can be used as queue and stack at the same time. In low-level, the following is the code that I created.

First, create a class called RPN with a constructor that accepts a string expression. This is done by using a special method initialize. Create a class variable @expression that is set to the argument passed into the constructor. We use the method chomp to remove any leading or trailing spaces from the string.
class RPN

def initialize(expression)
@expression = expression.chomp
end

 Create a new method infix_to_postfix. 
def infix_to_postfix

  Create new arrays and assign them to local variables. Note: there is no @ prefix.
operands = Array.new
operators = Array.new
newexpression = Array.new

 Use the scan method to extract any part of the string that matches the regular expression and then push them into each respective array.
@expression.scan /\d/ do |operand|
operands.push(operand)
end
@expression.scan /[\+\-*\/]/ do |operator|
operators.push(operator)
end

 This is a while-loop here similar to Java but without the use of parentheses
while operands.length != 0 && operators.length != 0

  Use the shift method to dequeue the first element of an array.
  Use the push method to insert an element to the tail of an array.
  Use the length method to get the total number of elements in an array.
if newexpression.length < 2
newexpression.push(operands.shift)
newexpression.push(operands.shift)
end

firstOperator = operators.shift
secondOperator = operators.shift

  The reserved word nil is equivalent to null in Java.
  The method op1_preceeds_op2? returns true depending on operator precedence.
if secondOperator == nil || op1_preceeds_op2?(firstOperator, secondOperator)
newexpression.push(firstOperator)

   Use the method unshift to return the element in front of the array.
operators.unshift(secondOperator) if secondOperator != nil
newexpression.push(operands.shift) if operands.length != 0
else
newexpression.push(operands.shift)
operators.unshift(firstOperator)
newexpression.push(secondOperator)
end
newexpression.push(operators.shift) if operators.length == 1
end

 Returning the value of the method is similar to Java.
return newexpression
end

This method used above is kind of straightforward. As a rule, * and / are evaluated in the order they appear in the expression and takes precedence over + and -. The latter are also evaluated in the order they appear in the expression. Use =~ to compare whether a given string matches a regular expression. There is another method out there but I find this one easier.
def op1_preceeds_op2?(op1, op2)
return true if op1 =~ /[*|\/]/ && (op2 =~ /[*|\/]/ || op2 =~ /[\+|\-]/)
return true if op1 =~ /[\+|\-]/ && op2 =~ /[\+|\-]/
return false
end

 This is the method that evaluates the post-fix expression.
def evaluate

Calling a method without parameters can be done by indicating the method name only in contrast to Java which additionally uses ().
postfixEx = infix_to_postfix
operands = Array.new
             
This is the for-loop which iterates through each element of the array. The current value is stored in the variable between two pipes.
postfixEx.each do |op|
if op =~ /\+|\-|\*|\//
if operands.length >= 2

     Use the method pop to get the last element of the array.
secondOperand = operands.pop
firstOperand = operands.pop

     The method calculate does the calculation obviously.
result = calculate(firstOperand, op, secondOperand)
operands.push(result)
end
else
operands.push(op)
end
end
return operands
end

def calculate(operand1, operator, operand2)

This is like switch in Java.
Here lies one of the main differences between Ruby and Java. In Ruby, data types are not declared. It might offer some kind of convenience, but in certain cases, you have to explicitly specify the data type especially when evaluating mathematical expressions. This is done by using the method to_f which converts the string into float. There are other methods such as to_i and to_s that serve the purpose of data type conversion into integer and string respectively.
case operator
when "+"
return operand1.to_f + operand2.to_f
when "-"
return operand1.to_f - operand2.to_f
when "*"
return operand1.to_f * operand2.to_f
when "/"
return operand1.to_f / operand2.to_f
end
end
end

To run the code, you just simply have to write the following lines outside of the RPN class.

A new object of RPN is created and assigned to a variable. The method evaluate is invoked.
rpn = RPN.new("7 + 5 - 3 / 6 * 7 + 4 / 3")  
puts rpn.evaluate

Comments

Popular posts from this blog

XML Schema and JSON Schema Validation in Mule 4

Parsing a JSON String and Converting it to XML in TIBCO

Using XML To Java in TIBCO BW