CSC/ECE 517 Fall 2011/ch1 2a av: Difference between revisions
(→Basics) |
No edit summary |
||
(5 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
Ruby is a dynamic, reflective, object oriented, single-pass interpreted programming language. Ruby was influenced by Perl, Smalltalk, Eiffel and Lisp. It supports functional, imperative, reflective, object oriented and many other programming paradigms. | Ruby is a dynamic, reflective, object oriented, single-pass interpreted programming language. Ruby was influenced by Perl, Smalltalk, Eiffel and Lisp. It supports functional, imperative, reflective, object oriented and many other programming paradigms. | ||
Ruby is said to incorporate the "Principle of | Ruby is said to incorporate the "Principle of Least Surprise" or "Principle of Least Astonishment". | ||
==History== | ==History== | ||
Ruby was created in 1993 in Japan, by Yukihiro | Ruby was created in 1993 in Japan, by Yukihiro "Matz" Matsumoto. The intent of developing Ruby was to have a new language that balance functional programming and iterative programming. Matsumoto has also stated that he wanted a scripting language that was more powerful than Perl and more Object Oriented than Python.(ref) | ||
===Naming=== | ===Naming=== | ||
Line 101: | Line 101: | ||
One can also use Ruby online at http://www.tryruby.org/. | One can also use Ruby online at http://www.tryruby.org/. | ||
==Ruby vs Java == | |||
{| class="wikitable" border="1" | |||
|- | |||
! Ruby | |||
! Java | |||
|- | |||
| Interpreted language | |||
| Compiled language | |||
|- | |||
| Dynamic Typing | |||
| Static typing. | |||
|- | |||
| Purely OO. Everything is an object | |||
| There are primitives and objects. | |||
|- | |||
| Unbounded Polymorphism | |||
| Inheritances and interfaces used. | |||
|- | |||
| Native support for Regular Expressions | |||
| Not native support; java.util.regex provides it. | |||
|- | |||
| Nil is an object which says there is no object | |||
| Null indicates the absence of objects | |||
|- | |||
| Methods calls are treated as messages at runtime | |||
| Method calls are compiled | |||
|- | |||
| Native support for hashes | |||
| Hashes are suported by Collections framework | |||
|} | |||
==Interactive Ruby Browser - IRB== | ==Interactive Ruby Browser - IRB== | ||
Line 118: | Line 150: | ||
==Basics== | |||
Ruby code can be directly evaluated in the Interactive Ruby Browser. The results are immediately returned in the IRB. Ruby code is saved in files with the extension ''.rb'' and can be run by invoking the command ‘'''ruby filename.rb'''’ | Ruby code can be directly evaluated in the Interactive Ruby Browser. The results are immediately returned in the IRB. Ruby code is saved in files with the extension ''.rb'' and can be run by invoking the command ‘'''ruby filename.rb'''’ | ||
Line 157: | Line 189: | ||
The last statement in the function is the return value. There is no need for an explicit '''return''' . And all statements return a value. | The last statement in the function is the return value. There is no need for an explicit '''return''' . And all statements return a value. | ||
==Dynamic Typing== | |||
Variables in Ruby are not given a type. Their “type” depends upon the | Variables in Ruby are not given a type. Their “type” depends upon the | ||
Line 185: | Line 217: | ||
>>String | >>String | ||
==Arrays== | |||
An array in Ruby is an ordered list of elements. Simple examples illustrate the use of arrays: | An array in Ruby is an ordered list of elements. Simple examples illustrate the use of arrays: | ||
Line 208: | Line 240: | ||
>> 10 | >> 10 | ||
==Hashes== | |||
Ruby has syntax support for hashes. Wondering what’s a “hash”? It's an “array” that can be indexed by arbitrary keys. Hashes can be created in three ways. | Ruby has syntax support for hashes. Wondering what’s a “hash”? It's an “array” that can be indexed by arbitrary keys. Hashes can be created in three ways. | ||
Using the keyword '''new''' , | |||
h = Hash.new | h = Hash.new | ||
h["a"] = 'b' | h["a"] = 'b' | ||
Line 219: | Line 251: | ||
>>b | >>b | ||
Using special syntax '''Hash[ ]''' or | |||
numbers = Hash['one', 1, 'two', 2, 'three', 3, 'four', 4] | numbers = Hash['one', 1, 'two', 2, 'three', 3, 'four', 4] | ||
Line 237: | Line 269: | ||
>>New Delhi | >>New Delhi | ||
==Ruby | ==Blocks== | ||
{| | Block is just a bunch of code grouped together. They are essentially objects, just like any other in Ruby, i.e They can be passed to other methods and other blocks as values, They take in values depending on the context in which it is used. They are also very useful to make the code more modular. <BR> | ||
For example, | |||
def multiply | |||
number=8 | |||
puts number | |||
| | end | ||
| | |||
It is a simple function that takes one parameter, multiplies it by a constant, display the result. Now, it looks fine. But, in the case when you want to change the '5' to '6', in this method, one has to locate the function and change it.<br> | |||
But, blocks offer a alternative to that. ''Yield'' is used to transfer the control from the method to a 'block' and back to the function again.<br> | |||
|- | |||
def multiply | |||
yield(8) | |||
| | end | ||
| | multiply{ |input| puts input } | ||
| | |||
Here as soon as the execution reaches yield, it is transferred to the block written later, and does the appropriate action. | |||
Thsis example may not be a convincing example. One may argue that the same can be achieved by delegating the task to another method. But blocks are easier because, you dont have to keep track of the other method. | |||
<br> | |||
So, you may choose to make the function do, | |||
multiply{ |input| puts input*input*input } | |||
without having to refer back to the original function. The above is a good example of a block. It is a standalone set of code, that associates itself with the method ''multiply''.<br><br> | |||
| | Now, it is also possible to use blocks with parametrized functions,<br> | ||
| | |||
| | def displayNumber( parameter ) | ||
|} | yield( parameter ) | ||
end | |||
displayNumber(10){ |num| puts num } | |||
The above can be extended to two or more parameters<br> | |||
To compare it with other languages, They are similar to the following, | |||
* Anonymous inner classes in Java | |||
* Anonymous functions in Perl | |||
* Lambda's in Python | |||
* Function objects in C++ | |||
* Fucntion pointers in C | |||
It is important to note that, since blocks provide a way to modify the way a method behaves in run-time, it is a very important facet of the polymorphism in Ruby. | |||
==Iterators== | |||
Iterator is an object which is used to traverse through a collection of data. It pretty much means the same thing as it does in java. But the tricky part is that, unlike java, iterators are defined in the collection and is simply put to use when the particular iterator is used. <br> | |||
There are a few iterators in ruby<br> | |||
* each | |||
* times | |||
* upto and step | |||
* each_index | |||
[1,2,3,4,5].each{ |i| puts i*i } | |||
1 | |||
4 | |||
9 | |||
16 | |||
25 | |||
The above snippet features a block and an iterator which passes the block to each object in the collection. So, the ''i*i'' is passed to all the objects, thus effectively squaring all of them. | |||
5.times{ |i| puts i*i } | |||
1 | |||
4 | |||
9 | |||
16 | |||
25 | |||
So, this effectively produces the same result as the above, but shall be useful when one has to repeat a task for a '''n''' number of times.<br> | |||
Its quite annoying to always start from the number 1 as in the case of Times iterator. One may wish to start from a number other than 1. For such cases there is the upto iterator. | |||
4.upto(10) { |i| puts i*i } | |||
16 | |||
25 | |||
36 | |||
49 | |||
64 | |||
81 | |||
100 | |||
The above prints the squares of all numbers from 4 to 10. So basically, 'upto' is 'times' iterator, with a lower bound.<br> | |||
Now in the kinds of situation where we might need to skip a number two as we progress, the 'step' iterator. | |||
2.step(20,2) { |i| puts i*i } | |||
4 | |||
16 | |||
36 | |||
64 | |||
100 | |||
144 | |||
196 | |||
256 | |||
324 | |||
400 | |||
The above piece of code, as you might have guessed, displays the square of all even numbers from 2 to 20, inclusive. | |||
==Closures== | |||
Assume we have been delegated the task of writing a method that takes a parameter and multiplies it by 6. We would probably go ahead and do the following.<br> | |||
def multByThree ( n ) | |||
n*6 | |||
end | |||
=> nil | |||
So far so good. But if we are delegated additional tasks to write multiplier functions to multiply by 1,2,3,4,5...,100 it would be a rather boring and tedious job to create 100 functions to the effect. But ruby has a very elegant solution to it. Closures.<br> | |||
Closure is a function with two additional properties | |||
* It stores the value of all the variables that were in its scope at time of creation. | |||
* It can be passed around as an object. | |||
In layman terms, a closure is a function that returns another function with atleast one less argument. | |||
def multByN ( n ) | |||
closure = lambda { |x| n*x } | |||
end | |||
=> nil | |||
obj = multByN( 4 ) | |||
obj.call 5 | |||
=> 20 | |||
Thus we have created a closure which is C(N,X) = N*X and having passed the parameter 4 on creation, the function F(x) = 4*x is returned by the closure. The ''lambda'' in the closure definition refers to a lambda function, which has its roots in functional programming. | |||
It basically is used to create functions dynamically which is exactly the purpose of a closure as well. | |||
==Currying== | |||
Currying is very similar to closures. It is basically used to create a function by fixing some value. But the difference is that, closures ''create'' and ''return'' lambda functions; while currying takes a normal function, and makes a lambda function out of it. | |||
It does something like this. | |||
f(x,y) ==currying==> f'(2,y). | |||
The two was nothing special. The point was to mention that after currying we get back a function with atleast one parameter lesser than the original function. In other words, currying fixes one or more parameters of a function and returns a new function based on the original function.<br> | |||
This is achieved, again by lambda method, just like closures. | |||
def multiply (x,y) | |||
val = x*y | |||
val | |||
end | |||
=> nil | |||
mulby2 = lambda { |x| multiply(x,2) } | |||
mulby3 = lambda { |x| multiply(x,3) } | |||
mulby2.call 2 | |||
=> 4 | |||
mulby3.call 2 | |||
=> 6 | |||
So, one may notice that, currying takes the existing function multiply(x,y) and curries it to form new functions mulby2(x) and mulby3(x). | |||
Latest revision as of 07:51, 6 October 2011
Introduction to Ruby
Ruby is a dynamic, reflective, object oriented, single-pass interpreted programming language. Ruby was influenced by Perl, Smalltalk, Eiffel and Lisp. It supports functional, imperative, reflective, object oriented and many other programming paradigms.
Ruby is said to incorporate the "Principle of Least Surprise" or "Principle of Least Astonishment".
History
Ruby was created in 1993 in Japan, by Yukihiro "Matz" Matsumoto. The intent of developing Ruby was to have a new language that balance functional programming and iterative programming. Matsumoto has also stated that he wanted a scripting language that was more powerful than Perl and more Object Oriented than Python.(ref)
Naming
The terms "Coral" and "Ruby" were the two proposed names for the language. Matsumoto's choose the term "Ruby" because it was one of his colleague’s birthstone.
Releases and Versions
Ruby 0.95 was the first public release in 1995. Three more versions were released immediately.
Ruby 1.0 was released in 1996.
Ruby 1.3 was the next release in 1999.
Since then several versions of Ruby have been released with added concepts and features.
The recent stable version is Ruby 1.9.2 which has significant improvements over Ruby 1.8 series.
Currently, Ruby 1.9.3 is under development with a dual-license of Ruby and BSD.
Ruby on Rails
With the release of Ruby on Rails in 2005, an MVC based web development framework written in Ruby, the new language gained mass acceptance and became famous.
In 2006, active user groups were formed in the world's major cities and several Ruby related conferences were held, making it one of the most popular and widely acceptable language. In 2007, TIOBE programming language popularity index (link) state Ruby as the 10th most popular language.
Features
1. Purely object oriented
Ruby is a purely object oriented language. There are no primitives. Everything is an object. For example, Integers, Strings, Arrays are all objects.
2. Dynamically types
Variables in Ruby are not assigned a type. The programmer need not declare the variable with its type before use. The type of the variable is given the type of the value it is assigned. Unlike statically types languages, there is no static compile time type checking in Ruby.
3. Blocks and Closures
Ruby supports functional programming paradigm by having blocks and closures. Blocks and Closures are explained in detail below.
4. Built in Iterators
Ruby supports built-in iterators that are used to traverse over a collection. Built-in iterators make programming easier and code shorter.
5. Multiple Inheritance through Mixins
The disadvantages of multiple inheritance are overcome in Ruby through the use of Modules and Mixins. These are similar to interfaces in Java.
6. Unbounded Polymorphism - "Duck Typing"
A method can be invoked on a variable whenever the type of object assigned to the variable has that method defined on it. This means that if the parameter passed to a method supports the methods invoked on it by the called method, then the calls work. This is unbounded polymorphism, which can only be found in dynamically typed languages.
7. Reflection
Reflection is referred to be the ability to introspect or examine the aspects of the program from within the program itself. Reflection allows program entities to discover things about themselves through introspection. For example, an object can ask what its methods are, and a class can tell what its ancestors are. Using refection, we can examine particular objects and decide which for their methods to call at run-time, even if the class of the object didn't exist when we first wrote the code. The program can be modified at run-time.
8. Metaprogramming
Metaprogramming means writing code that writes code. This allows us to modify the behavior of a program at run time. Ruby has several metaprogramming facilities.
9. Syntax level Perl compatible for regular expressions
Ruby is a scripting as well as programming language. It is strongly influences by Perl and provides Perl compatible regular expression suport.
10. Built-in support for certain Design Patterns
Ruby also provides support for certain design patterns that programmers can use to solve the recurring design problems.
11. Mark-and-Sweep garbage collection
Ruby performs automatic mark-and-sweep garbage collection and frees the unreferenced memory. The programmer need not worry about memory management.
12. OS independent threading
The threading features that Ruby provides are independent of the underlying Operating System. This provides a great deal of flexibility.
13. Easy interface to C modules
Ruby provides interface to C modules which help programmers integrate C with Ruby.
14. Portable easily
Ruby was developed on GNU/Linux. It is portable and works on several types of UNIX, DOS, BeOS, OS/2, Mac OS X, Windows 95/98/Me/NT/2000/XP/Vista/7, etc.
Downloads and Installation
The main website for Ruby is http://www.ruby-lang.org/en/.
Ruby downloads and installation instructions can be found at http://www.rubylang.org/en/downloads/. Ruby can be installed by either building it from source or by using the one-click Ruby installer for Windows. Using the one-click Rails Installer for Windows, one can install Ruby and Rails together.
One can also use Ruby online at http://www.tryruby.org/.
Ruby vs Java
Ruby | Java |
---|---|
Interpreted language | Compiled language |
Dynamic Typing | Static typing. |
Purely OO. Everything is an object | There are primitives and objects. |
Unbounded Polymorphism | Inheritances and interfaces used. |
Native support for Regular Expressions | Not native support; java.util.regex provides it. |
Nil is an object which says there is no object | Null indicates the absence of objects |
Methods calls are treated as messages at runtime | Method calls are compiled |
Native support for hashes | Hashes are suported by Collections framework |
Interactive Ruby Browser - IRB
Once Ruby is installed, code can directly be evaluated using the IRB. IRB is an interactive command-line interpreter which can be used to test code quickly. The result of execution of the code is immediately returned in the IRB. It can be started in two ways:
1. Directly open the IRB by clicking on the Interactive Ruby icon.
2. In the command prompt, type "irb" to start a new IRB session.
Other Editors for Ruby
Ruby is supported by Eclipse. It has an exclusive Ruby perspective. However, the eclipse plug-in for ruby has to be installed.
RubyMine is another Ruby editor which is commonly used for Ruby on Rails - Web development Framework. Rubymine is considered to be the standard platform for developing applications in Ruby on Rails.
Basics
Ruby code can be directly evaluated in the Interactive Ruby Browser. The results are immediately returned in the IRB. Ruby code is saved in files with the extension .rb and can be run by invoking the command ‘ruby filename.rb’
The files can be made executable on UNIX systems (without typing ruby) by adding the shebang line as the first line in the file
#!/usr/bin/ruby
The “Hello, world” program can be written as
puts “Hello, world!” >>Hello, world!
Functions are defined by the keyword def. The keyword end is equivalent to the closing parenthesis in other languages.
def ruby(i = 5) i.times do puts "Ruby" end end ruby >>Ruby >>Ruby >>Ruby >>Ruby >>Ruby
The arguments to a function can be initialized. Parentheses in a function call are optional. So we can write hello ( 4 ) or hello 4 . Both mean the same.
Function arguments can be aggregated –
def greet(*names) puts "Hello #{names.join(', ')}" end greet('Amy', 'Beth', 'John') >>Hello Amy, Beth, John
The last statement in the function is the return value. There is no need for an explicit return . And all statements return a value.
Dynamic Typing
Variables in Ruby are not given a type. Their “type” depends upon the value assigned to them.
PI = 3.14159 #By convention, constants have uppercase names a = 5 b = 23 c = [1,1,2,3,5,8] #This is an array declaration
Comments in Ruby start with a # and run to the end of the line. Dynamic typing makes many features possible, such as unbounded polymorphism and blocks, which are not available in statically typed languages such as Java and C++.
Everything is an object since Ruby is purely object-oriented. Even numbers and strings are objects.
5.succ >>6 "object".upcase >>OBJECT [3,1,4,2].sort >>[1,2,3,4] “Ruby”.class >>String
Arrays
An array in Ruby is an ordered list of elements. Simple examples illustrate the use of arrays:
a = [] # empty array b = [1, 2, 3] # array of FixNums c = ['Hello', 'Ruby'] #array of strings
Special syntax to create an array of strings without the quotes:
d = %w{Hello Ruby}
Appending elements to an Array –
b << 4 >>[1, 2, 3, 4]
Array indexes start from zero. Negative indexes count backward. So b[‐1] refers to the last element of b . Arrays can also be created using the new keyword. new is used to create objects of any type. More about new will follow later.
n = Array.new n << 10 puts n >> 10
Hashes
Ruby has syntax support for hashes. Wondering what’s a “hash”? It's an “array” that can be indexed by arbitrary keys. Hashes can be created in three ways.
Using the keyword new , h = Hash.new h["a"] = 'b' puts h['a'] >>b
Using special syntax Hash[ ] or numbers = Hash['one', 1, 'two', 2, 'three', 3, 'four', 4] puts numbers['three'] >>3
3. Using { }
capitals = { "France" => "Paris", "Russia" => "Moscow", "Japan" => "Tokyo", "India" => "New Delhi", "USA" => "Washington, DC" } puts capitals["India"] >>New Delhi
Blocks
Block is just a bunch of code grouped together. They are essentially objects, just like any other in Ruby, i.e They can be passed to other methods and other blocks as values, They take in values depending on the context in which it is used. They are also very useful to make the code more modular.
For example,
def multiply number=8 puts number end
It is a simple function that takes one parameter, multiplies it by a constant, display the result. Now, it looks fine. But, in the case when you want to change the '5' to '6', in this method, one has to locate the function and change it.
But, blocks offer a alternative to that. Yield is used to transfer the control from the method to a 'block' and back to the function again.
def multiply yield(8) end multiply{ |input| puts input }
Here as soon as the execution reaches yield, it is transferred to the block written later, and does the appropriate action.
Thsis example may not be a convincing example. One may argue that the same can be achieved by delegating the task to another method. But blocks are easier because, you dont have to keep track of the other method.
So, you may choose to make the function do,
multiply{ |input| puts input*input*input }
without having to refer back to the original function. The above is a good example of a block. It is a standalone set of code, that associates itself with the method multiply.
Now, it is also possible to use blocks with parametrized functions,
def displayNumber( parameter ) yield( parameter ) end displayNumber(10){ |num| puts num }
The above can be extended to two or more parameters
To compare it with other languages, They are similar to the following,
- Anonymous inner classes in Java
- Anonymous functions in Perl
- Lambda's in Python
- Function objects in C++
- Fucntion pointers in C
It is important to note that, since blocks provide a way to modify the way a method behaves in run-time, it is a very important facet of the polymorphism in Ruby.
Iterators
Iterator is an object which is used to traverse through a collection of data. It pretty much means the same thing as it does in java. But the tricky part is that, unlike java, iterators are defined in the collection and is simply put to use when the particular iterator is used.
There are a few iterators in ruby
- each
- times
- upto and step
- each_index
[1,2,3,4,5].each{ |i| puts i*i } 1 4 9 16 25
The above snippet features a block and an iterator which passes the block to each object in the collection. So, the i*i is passed to all the objects, thus effectively squaring all of them.
5.times{ |i| puts i*i } 1 4 9 16 25
So, this effectively produces the same result as the above, but shall be useful when one has to repeat a task for a n number of times.
Its quite annoying to always start from the number 1 as in the case of Times iterator. One may wish to start from a number other than 1. For such cases there is the upto iterator.
4.upto(10) { |i| puts i*i } 16 25 36 49 64 81 100
The above prints the squares of all numbers from 4 to 10. So basically, 'upto' is 'times' iterator, with a lower bound.
Now in the kinds of situation where we might need to skip a number two as we progress, the 'step' iterator.
2.step(20,2) { |i| puts i*i } 4 16 36 64 100 144 196 256 324 400
The above piece of code, as you might have guessed, displays the square of all even numbers from 2 to 20, inclusive.
Closures
Assume we have been delegated the task of writing a method that takes a parameter and multiplies it by 6. We would probably go ahead and do the following.
def multByThree ( n ) n*6 end => nil
So far so good. But if we are delegated additional tasks to write multiplier functions to multiply by 1,2,3,4,5...,100 it would be a rather boring and tedious job to create 100 functions to the effect. But ruby has a very elegant solution to it. Closures.
Closure is a function with two additional properties
- It stores the value of all the variables that were in its scope at time of creation.
- It can be passed around as an object.
In layman terms, a closure is a function that returns another function with atleast one less argument.
def multByN ( n ) closure = lambda { |x| n*x } end => nil obj = multByN( 4 ) obj.call 5 => 20
Thus we have created a closure which is C(N,X) = N*X and having passed the parameter 4 on creation, the function F(x) = 4*x is returned by the closure. The lambda in the closure definition refers to a lambda function, which has its roots in functional programming. It basically is used to create functions dynamically which is exactly the purpose of a closure as well.
Currying
Currying is very similar to closures. It is basically used to create a function by fixing some value. But the difference is that, closures create and return lambda functions; while currying takes a normal function, and makes a lambda function out of it. It does something like this.
f(x,y) ==currying==> f'(2,y).
The two was nothing special. The point was to mention that after currying we get back a function with atleast one parameter lesser than the original function. In other words, currying fixes one or more parameters of a function and returns a new function based on the original function.
This is achieved, again by lambda method, just like closures.
def multiply (x,y) val = x*y val end => nil
mulby2 = lambda { |x| multiply(x,2) } mulby3 = lambda { |x| multiply(x,3) }
mulby2.call 2 => 4 mulby3.call 2 => 6
So, one may notice that, currying takes the existing function multiply(x,y) and curries it to form new functions mulby2(x) and mulby3(x).