[Basics] 3 Building Blocks

    

    We have now covered variables, and constants, the next common area of any programming language is being able to use these variables and constants in logical procedures to receive a required functionality.

    Computers, and the programs which run on them are not intelligent at all. People are actually intelligent, and the folks who program firmware, and low-level drivers, operating systems and such are pure geniuses. A program, including your Ruby code only does what you tell it to. 

    In life, we make decisions, not only do we make decisions, we examine scenarios and base our decisions on reasoning. This method of action can be implemented pragmatically. This is called Flow Control. Lets review some logical scenarios and convert them to ruby code. We will also cover some of the logical constructs which make up the Ruby language. After this posting, you should be able to write some Ruby code!! =)


If/Elsif/Else and Unless

    Ruby as does many other languages contains the if,elsif,else logic. This logic tests a condition for truth. If truth is found, the following code is executed . if the statement, condition or variable is found to be false execution continues to any next elsif statements and further on to an else catch all statement if no conditions are true in the elsif condition tests. The condition can be code itself, it can be an expression, it can be a statement. So long as the condition is true the code after that clause is executed. 

    What makes a statement true? This is hard to explain in English to me. But take for instance the following statements and their preceding labels. 

#True
5+5 = 10

#False
5+5 = 5

#True
"This" == "This"

#False
"This" == "That"

#Get the point? Truth means the statement checks out.

    In ruby an if statement starts with the keyword if, immediately following if is a condition. After your condition you are testing for truth, comes the code you want to execute. The if statement requires an end statement to close the block. A elsif or else keyword is not required. However elsif follows the same logic as if, with the keyword elsif followed by a condition to test, followed by the code to execute if a truth is met. The else statement does NOT have a condition following it, it simply says which code to run if all else is not true. Again we always close these blocks with the keyword end. See below examples.
    

    1) If you are 10 years old or older you can get a ticket, if you are under 10, you are not allowed to buy tickets.

>> age = 29
=> 29
>> if age >= 10
>>   puts "old enough"
>>   else
?>     puts "you are too young!!"
>>   end
old enough
=> nil
>> age = 7 
=> 7
>> if age >= 10
>>   puts "old enough"
>>   else
?>     puts "you are too young!!"
>>   end
you are too young!!
=> nil

In this case, we first defined a variable called age, a numeral value of 29. We then went on into an if statement with one condition, if the variable age is greater than or equal to 10 then print to standard output the string literal "old enough", otherwise print to standard output the string literal "you are too young!!". In the second piece We set the age variable to 10 and enter the same code. When the variable is 29 as we expected, we receive "old enough", on the second run however I set the age to 7 and that received "you are too young!!".

    2) If you are 10 years old older you can get a ticket, if you are between 7 and 9 you can not get a ticket however you can get an ice cream , if you are under 5 you are too young.

#in the case the age is 7.
>> age = 7
=> 7
>> if age >= 10 
>>   puts("old enough")
>>   elsif age >= 7 && age <= 9 
>>   puts ("not old enough, but take this ice cream kiddo!")
>>   else
?>     puts("not old enough, not even for iceys, beat it snot bag")
>>   end
not old enough, but take this ice cream kiddo!
=> nil

#In the case the age is 5.
>> age = 5
=> 5
>> if age >= 10
>>   puts("old enough")
>>   elsif age >= 7 && age <= 9
>>   puts ("not old enough, but take this ice cream kiddo!")
>>   else
?>     
?>     puts("not old enough, not even for iceys, beat it snot bag")
>>   end
not old enough, not even for iceys, beat it snot bag

#In the case the age is 10.
>> age = 10
=> 10
>> if age >= 10
>>   puts("old enough")
>>   elsif age >= 7 && age <= 9
>>   puts ("not old enough, but take this ice cream kiddo!")
>>   else
?>     puts("not old enough, not even for iceys, beat it snot bag")
>>   end
old enough


   
It should be noted that there is also another syntax of an if statement which makes use of something called an operator. The format for this version of an if statement is below: 

[condition to test] ? <code to execute if true> : <code if false>

so lets rewrite our code using this syntax which is way shorter.

>> age >=10 ? puts("old enough") : puts("not old enough get a life buddy")
old enough

Loads quicker no?

There is also an unless statement which is the opposite of an if statement. Testing for false conditions.

=> 10
>> unless age >= 20
>>   puts "yes"
>>   end
yes
=> nil

As we see the age is set to 10, and the test is for age being greater than 20 to be FALSE, in plain English it would read, unless age is greater than or equal to 20, print "yes".


Case/When

    Ruby has another control statement named case. Case can prove useful if you find your self writing endless elsif statements in an if/elsif/else block. Take this comparison into consideration. First the if/elsif/else version of this code.

>> today = "monday"
=> "monday"
>> if today == "monday"
>>   puts "i hate mondays"
>>   elsif today == "tuesday"
>>   puts "i hate tuesday more than monday"
>>   elsif today == "wednesday"
>>   puts "its hump day!!"
>>   elsif today == "thursday"
>>   puts "it's payday,, atleast for me"
>>   elsif today == "friday"
>>   puts "THANK BABY JESUS ITS FRIDAY"
>>   else
?>     puts "have a nice weekend"
>>   end
i hate mondays
=> nil

We can save ourselves some typing by using case/when

>> today = "monday"
=> "monday"
>> case today 
>>   when "monday"
>>   puts "i hate mondays"
>>   when "tuesday"
>>   puts "i hate tuesday more than monday"
>>   when "wednesday"
>>   puts "its hump day!!"
>>   when "thursday"
>>   puts "payday"
>>   when "friday"
>>   puts "tgif!!!"
>>   else
?>     puts "have a nice weekend"
>>   end
i hate mondays
=> nil

While/Until

We can also do more traditional loops with the while statement in ruby. Remember while is still testing for a condition to be true similar to if. Lets see by an example, lets see what while does and how it always looping and testing a condition, we will see a infinite loop here, this is generally NOT what you want to do, however for a point to be proven see below. 

counter = 1
while counter < 20
puts "The factor of #{counter} and #{counter} is equal to #{counter * counter}"

executing this code, loops indefinitely, it does so because we are testing a counter variable to be less than 20, if it's less than 20 we print to screen the sum of the counter multiplied by itself. HOWEVER notice, our counter variable at no point is actually changing here, so as the counter is ALWAYS 1, the loop will not stop until you hit CTRL+C 

>> counter = 1  
=> 1
>> while counter < 20
>>   puts "The factor of #{counter} and #{counter} is equal to #{counter * counter}"
>>   end
The factor of 1 and 1 is equal to 1
The factor of 1 and 1 is equal to 1
The factor of 1 and 1 is equal to 1
The factor of 1 and 1 is equal to 1
IRB::Abort^C: abort then interrupt!!
from /usr/lib/ruby/1.8/irb.rb:89:in `irb_abort'
from /usr/lib/ruby/1.8/irb.rb:255:in `signal_handle'
from /usr/lib/ruby/1.8/irb.rb:66:in `start'
from (irb):78:in `call'
from (irb):78:in `write'
from (irb):78:in `puts'
from (irb):78


Lets fix this up and actually INCREMENT our variable properly so that the loop test finally becomes false and the looping completes. This will also make our calculations the numbers more interesting as they will change on each iteration.

>> counter = 1
=> 1
>> while counter < 20
>>   puts "The factor of #{counter} and #{counter} is equal to #{counter * counter}"
>>   counter += 1
>>   end
The factor of 1 and 1 is equal to 1
The factor of 2 and 2 is equal to 4
The factor of 3 and 3 is equal to 9
The factor of 4 and 4 is equal to 16
The factor of 5 and 5 is equal to 25
The factor of 6 and 6 is equal to 36
The factor of 7 and 7 is equal to 49
The factor of 8 and 8 is equal to 64
The factor of 9 and 9 is equal to 81
The factor of 10 and 10 is equal to 100
The factor of 11 and 11 is equal to 121
The factor of 12 and 12 is equal to 144
The factor of 13 and 13 is equal to 169
The factor of 14 and 14 is equal to 196
The factor of 15 and 15 is equal to 225
The factor of 16 and 16 is equal to 256
The factor of 17 and 17 is equal to 289
The factor of 18 and 18 is equal to 324
The factor of 19 and 19 is equal to 361
=> nil

Notice the only change is the addition of an increment method. += is shorthand for typing "counter + 1". It is saying increment this value by 1 or add one to this numeric value if you will. When the variable reaches the value of 19 and goes to the next execution it is tested and 20 is seen, therefore the loop exits as the condition is less than 20. 

A completely reverse way of looking at things is to introduce the until statement. Lets look at it.

>> counter = 1
=> 1
>> until counter == 20
>>   puts "The factor of #{counter} and #{counter} is equal to #{counter * counter}"
>>   counter += 1
>>   end
The factor of 1 and 1 is equal to 1
The factor of 2 and 2 is equal to 4
The factor of 3 and 3 is equal to 9
The factor of 4 and 4 is equal to 16
The factor of 5 and 5 is equal to 25
The factor of 6 and 6 is equal to 36
The factor of 7 and 7 is equal to 49
The factor of 8 and 8 is equal to 64
The factor of 9 and 9 is equal to 81
The factor of 10 and 10 is equal to 100
The factor of 11 and 11 is equal to 121
The factor of 12 and 12 is equal to 144
The factor of 13 and 13 is equal to 169
The factor of 14 and 14 is equal to 196
The factor of 15 and 15 is equal to 225
The factor of 16 and 16 is equal to 256
The factor of 17 and 17 is equal to 289
The factor of 18 and 18 is equal to 324
The factor of 19 and 19 is equal to 361


While and Until provide looping constructs. As with the if statement, they can be typed on one line, as well they are completed with an end statement.

Iterators & Enumerable Objects

To iterate is to proceed through a series or collection. Enumerable is an attribute which means countable. Ruby lets you very easily perform both using it's built in controlling methods. 

1) upto - this is an iterator method. It returns an enumerable object, a code block can be called to process per iteration.

Ex: 
>> 1.upto(100) {|number| puts "Half of #{number.to_s} is #{number / 2}" } 
Half of 1 is 0
Half of 2 is 1
Half of 3 is 1
Half of 4 is 2
Half of 5 is 2
Half of 6 is 3
Half of 7 is 3
Half of 8 is 4
...

This actually goes up until 100 but I truncated the output here for brevity. One thing you may notice is the fact that the math is incorrect. Slap! That is Ruby again and it's classes. Remember we are working with whole numbers here, no decimal places. We go from 1 - 100. So when we perform a mathematical calculation dividing an odd number by 2, we are expecting a number with a decimal place. However as stated, these are Fixed Number classes, and not Floating point. If we wanted exact math however, we can get it, lets convert the numbers to float so that our math is correct and people don't think we are idiots =)

Ex: (redone)
>> 1.upto(100) {|number| puts "Half of #{number.to_s} is #{number.to_f / 2.0}" } 
Half of 1 is 0.5
Half of 2 is 1.0
Half of 3 is 1.5
Half of 4 is 2.0
Half of 5 is 2.5
Half of 6 is 3.0
Half of 7 is 3.5
Half of 8 is 4.0
Half of 9 is 4.5
Half of 10 is 5.0
Half of 11 is 5.5
Half of 12 is 6.0
Half of 13 is 6.5
Half of 14 is 7.0
Half of 15 is 7.5
Half of 16 is 8.0
Half of 17 is 8.5
Half of 18 is 9.0
Half of 19 is 9.5
Half of 20 is 10.0
...
Good deal?

2) downto - We will not stick on this one long as it is the exact inverse of the previous iterator method I have shown. It is called downto, it's usage is synonymous with that of the upto method.

3) step - Step allows you to count by a certain number. Like by 2's for instance. You simply supply the max number and the stepping number.

Ex:
>> 0.step(10,2) {|x| puts x}
0
2
4
6
8
10
=> 0

4) times - times is a simple looping construct. If you have ever used zsh, it is similar to the repeat function. 

Ex:
>> 2.times { puts "yes"}
yes
yes
=> 2

Simple enough yes?



Enclosures

    Enclosures, or code blocks in ruby are denoted by the code between do and end it can also be enclosed in surrounding curly brackets {}. We can see this below. Something else you will see is a brief example of dealing with an array. Code blocks receive data from other calls and iterate over that data. Actually the Array class comes with methods specifically for iterating it's members. Lets look at this...

#This is how to statically define an array. There are other ways to create Array objects with Array.new being one. This Array is stored in a variable called Array.

>> array = [4,5,6]
=> [4, 5, 6]

#We call the each method on our array variable, we call class on the returned object. This object is of Enumeberable::Enumerator class, it is a class inside of a module. We will cover modules and classes more in other posts.
>> array.each.class
=> Enumerable::Enumerator

#Iterating the enumerator object and printing the members as strings.

>> array.each do |i|
?>     puts i.to_s
>>   end
4
5
6
=> [4, 5, 6]

So we know what the returned object is, it's an Enumerator object, so this means we can then call the each method upon this object directly. Each method takes each member in the array variable and sends it to our code block. The |i| is called a "chute" it is saying to put the data from *this* iteration of each, into the variable defined between the chute. We then go on to print each member (which is actually a FixNum class) as a string, by calling the to_s method on each member as we step through iterations. The end statement denotes the end of this processing.

    As previously stated, do and end is interchangeable with {}. Lets see it in action.

>> array.each {|i| puts i.to_s }
4
5
6
=> [4, 5, 6]

There is a way to actually save a code block and call on it at a later time by referencing the variable and a call statement. This is possible by way of a method named lambda. Lambda creates a Proc object which is "saved code block" in object form. More on this later, but a taster below.

>> multiply_by_myself = lambda {|x| puts x * x }
=> #<Proc:0x00007f5333429248@(irb):55>
>> multiply_by_myself.call(5)
25

 
Code Blocks in ruby 1.8.x share the same scope as their calling code, however in ruby 1.9.2 there is a change and code blocks have their own scope. 

Methods


    Methods are very similar to functions in procedural programming with the exception that methods by programmatic definition act on objects. Ruby methods indeed act on objects however one could simply write his own methods in the global scope of ruby without using the notion of classes or objects. This would be counter productive but it is possible.  

    Methods (as well as constants, variables, and etc) reside inside of classes & modules in ruby. This of course is due the object oriented nature of the language. Just about every function you use in Ruby comes from a class. In the previous sections you may have seen us calling functions directly on objects or on variables holding an object. We use the "." to denote that we are calling the function after the dot on the object preceding the dot. However IF the class or module is already available to the present scope of the program... there is no need to specify the dot and what is previous to it. For instance. Lets take Kernel module. And lets look at the puts and gets methods.

>> puts "test"
test
=> nil
>> gets
data
=> "data\n"

    How is it that I am able to call puts and gets with no object preceding and no dot delimiter for the object and method?

    Simple as previously stated the Kernel module is always present. The methods gets, and puts come from this module. The Kernel module is "Mixed In" with the Object class. We will talk about modules and mix in's in another post. A module is very much like a class however objects are not created from modules. However methods and even other classes can reside inside of a Module. First lets reiterate calling a function on an object.

#Calling the inspect method on the Object Object (yes I said that twice, it is an object of the object class)

>> Object.inspect
=> "Object"

#Calling a method in the KERNEL MODULE (not class).
>> Kernel.puts("Si Senor")
Si Senor
=> nil

You can see it is the same methodology. And the Kernel modules puts method can be called without referencing the module.

#Calling puts directly without referencing the module.
>> puts "test"
test
=> nil

#calling INSPECT directly without referencing an object
>> inspect
=> "main"

Both above examples are capable because the Kernel Module and Object Class is available to all code in Ruby.

    We have covered how to use methods in Ruby however lets look at how to make them. Methods are defined by the def keyword and they end with the end keyword. all code between is legal code. Lets look at a simple example of creating a method.

>> def say_your_name
>>   puts "your name"
>>   end
=> nil
>> say_your_name
your name
=> nil

    We defined a very simple method which simply prints a string to the screen. There is no logic involved. A method similar to a command line program (or any program for that matter) can process arguments. Arguments are additional data passed into a method that the method can process or use to control processing. Methods which take arguments are most likely more scalable and usable than those which don't. Lets make our silly method a little less silly now.

>> def say_your_name(name)
>>   puts name
>>   end
=> nil
>> say_your_name("Aaron Samuel")
Aaron Samuel
=> nil

    We have now introduced a way for our method to process arguments. And boy does it make the code less stupid? You can also see how reusable this is. If the requirement was only for the program to say your name,, there you have it.

    Not only can we provide arguments, but we can provide default values for those arguments.

>> def say_your_name(name="Aaron Samuel")
>>   puts name
>>   end
=> nil
>> say_your_name()              
Aaron Samuel
=> nil

    Notice that without providing an argument the method returns Aaron Samuel. This is because we defined a default value for the local variable name. (Which as discussed in our variables talk is a local variable and local in scope to the say_your_name method). Another thing you may notice is I call say_your_name with empty parenthesis. In Ruby a function can be called with parenthesis enclosing it's arguments or you can also omit them. It is purely up to the developer or coder. However sometimes no parenthesis can look odd. There are also some edge cases where you will need to use parenthesis to not confuse the interpreter.

    You now know about methods, ruby flow control and logic statements, and enclosures. Have fun moving forward with my postings.



Comments