Ruby is a computer programming language with style and charm.
Programming in Ruby is like being hugged. This chap on the
left is a ruby-crowned kinglet, one of Ruby's trial mascots
(the other being some anime bint). I think it's pretty cute,
but I can't get over the desire to pluck and barbecue it.
Y'see, in Australia we have this barbecue chicken take away
joint called Red Rooster and their logo...
The following is an introduction to Ruby I wrote in third year
university for my fellow students because we were learning
Python. It's had a few updates since then.
Ruby Rant
Why do I think Ruby is neato?
- Blocks
-
Ruby has a beautiful and unified way of dealing
with closures, iterators, callbacks and a number of
other miscellanous tasks. Smalltalk had it right.
Unifying these things means I don't have to remember
all the different ways to do these things.
To explain, in Ruby, if you include a piece of code
surrounded by curly braces after a method, that piece
of code will be passed to the method as a 'block' (of
code). The method can call the block at any time,
passing any number of parameters to it.
By identifying and formalising this mechanism in
the language, a number of common tasks which can be
performed using blocks suddenly have a uniform syntax.
Not only that, by allowing the programmer to write
custom methods which are able to use blocks, the
programmer can refactor program flow into methods. I
cannot express to you the geeky joy of putting a
complex piece of behaviour into a convenient piece of
code which is easily called upon later to perform that
behaviour.
- Iteration
-
Let me give you an example. The following code
(deep breath) implements the textbook definitions of inorder,
preorder and postorder traversal of trees of arbitrary
order. Each method is a few lines of code, and clearly
labels that code's behaviour with its name.
1:
2: class Tree
3: def initialize(value, *children)
4: @value, @children = value, children
5: end
6:
7:
8: def traverse_inorder(&block)
9: first = @children[0]
10: rest = (@children[1..@children.size] or [])
11:
12: first.traverse_inorder(&block) if first
13: yield @value
14: for c in rest
15: c.traverse_inorder(&block)
16: end
17: end
18:
19:
20: def traverse_preorder(&block)
21: yield @value
22: for c in @children
23: c.traverse_preorder(&block)
24: end
25: end
26:
27:
28: def traverse_postorder(&block)
29: for c in @children
30: c.traverse_postorder(&block)
31: end
32: yield @value
33: end
34: end
35:
36:
37:
38:
39:
40:
41:
42:
43:
44: tree = Tree.new(4, Tree.new(2, Tree.new(1), Tree.new(3)), Tree.new(5))
45:
46: values = []
47: tree.traverse_inorder do |value|
48: values << value
49: end
50: puts "Inorder : " + values.join(", ")
51:
52: values = []
53: tree.traverse_preorder do |value|
54: values << value
55: end
56: puts "Preorder : " + values.join(", ")
57:
58: values = []
59: tree.traverse_postorder do |value|
60: values << value
61: end
62: puts "Postorder: " + values.join(", ")
63:
64:
65:
66:
67:
68:
|
Download the above code
- Pure OO / Unified Type Model
-
Some people don't see the benefit of this. It
doesn't mean we have to be like Java with a zillion
heavily documented classes for every tiny thing under
the Sun (joke!) and objects flying everywhere because
this OO stuff is a new paradigm so we'd better do
things differently.
No. It just means I can forget the difference
between types and classes and will never be forced to
uglify my code with conversions between the two. It
means I can use polymorphism without fear. It means I
can clone objects without tricks and traps. It means I
can always inherit from library classes. It means a
lot of little things which, to me, add up to a whole
lot of ease.
- Simple Syntax
-
Python. Variables prefixed with 'self.' are member
variables and you declare global ones with the
'global' keyword.
Ruby. Variables prefixed with '@' are member
variables and ones prefixed with '$' are global.
Everything else has local scope.
Python enthusiasts hate this, because 'self.'
apparently makes it more obvious that the variable is
a member. And it does... if you're used to Python.
I appreciate code readabilty as much as the next
person, but it's hardly an effort to remember '@'
means 'member' instead of remembering that 'self.'
means 'member'. This also avoids the need to declare
variables, whereas you still need to declare 'global'
variables sometimes in Python.
Ruby's syntax for class inheritance is thus:
class MyArray < Array Again, uses a
symbol, but isn't difficult.
- Data Hiding
-
I can make member methods or data private, so I can
model
OO design patterns if I want to. People are
writing implemenations
of design by contract in Ruby, for example. It's
nice to know that nobody's going to come along and
change your object's internal state without you
knowing about it.
- Customisability
-
At any time I can add/modify/delete methods from a
class. Even a library class. If I don't like Array's
include? method because I'm a Java
programmer, I can change it to has. If I
want to be able to pass an array of keys to a hash
(dictionary) and receive an array of sorted values, I
can add this method to Hash. I don't need to worry
about where to put it.
1:
2: class Hash
3:
4:
5:
6: def sorted_values(*keys)
7: return indices(*keys).compact.sort
8: end
9: end
10:
11: myhash = {1=>'A', 2=>'B', 3=>'C'}
12:
13: p myhash.sorted_values(3, 1, 2)
14:
15:
16:
17:
|
Download the above code
- Pseudocode Look and Feel
-
I appreciate that Python tries to make it so that
there is 'only one way to write a piece of code', but
frankly the whole indentation issue leaves me always
wondering where my statements belong. After two if
statements and a loop have ended, I'm always confused.
Maybe I'm just stupid. Ruby uses the keyword 'end' to
end blocks and avoid all related confusion. This also
has the benefit that if I don't like the way somebody
has formatted a file, I can highlight the entire thing
and tell my text editor to re-indent it all, without
fear of breaking it.
Ruby's philosophy is that if you write a piece of
Ruby code, it should run. What you think is
right, is what Ruby takes as what should be
right. That's why Ruby often looks like pseudocode.
There aren't many conventions you have to pick up
before your pseudocode starts looking like Ruby.
*grin*
A lot of Ruby coders are amazed at how they can
hack together something like they used to do in Perl,
have it run first time, and yet be able to read and
understand it the next day.
- Operator overloading
-
Syntactic sugar, but it means I can make my own
objects behave correctly when I
add/multiply/subtract/divide/compare/cast or print
them. Can improve code readability a great deal for
some tasks.
1: class Vector < Array
2: def initialize(*values)
3: push(*values)
4: end
5:
6:
7: def *(other)
8: if size == other.size
9: pos = 0
10: result = 0
11: for v in self
12: result += v*other[pos] if other[pos]
13: pos += 1
14: end
15: return result
16: else
17: raise "Attempting to find inner product of \
18: two vectors of different dimensions"
19: end
20: end
21:
22: def inspect
23: return "Vector:" + super
24: end
25:
26: def to_s
27: return "<" + join(", ") + ">"
28: end
29: end
30:
31: v1 = Vector.new(0, 1, 2)
32: v2 = Vector.new(4, 5, 6)
33: p v1
34: p v2
35: puts "%s.%s = %d" % [v1, v2, v1 * v2]
36:
37:
38:
39:
40:
41:
|
Download the above code
Language Comparisons by Others
Ruby Downloads
- Ruby Interpreter
-
The latest version of Ruby (for UNIX systems) can
be downloaded from http://www.ruby-lang.org.
There's also a Windows version.
- Ruby Applications and Libraries
The Ruby
Application Archive is a big list of applications and
libraries written with Ruby.
- Emacs Mode
-
The file you'll need for syntax highlighting and
indentation while editing Ruby code in Emacs/XEmacs is
called ruby-mode.el.
To install it, put it somewhere convenient, we'll
call that directory X. Now open up the
file in Emacs/XEmacs and select 'Byte-compile This
File' from the 'Emacs-Lisp' menu. Next edit your
.emacs file (or .xemacs/init.el for newer versions of
XEmacs) and insert the following at the end:
(autoload 'ruby-mode "ruby-mode" "Major mode for editing ruby scripts." t)
(setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))
(setq interpreter-mode-alist (append '(("ruby" . ruby-mode)) interpreter-mode-alist))
(setq load-path (append load-path '("X")))
Replace 'X' in the
above code with the directory where you put
ruby-mode.el. For example, if I put it in a
subdirectory from my login directory called
".elisp", I would replace X with
~/.elisp. Newer versions of XEmacs
likes to put these kind of user-added files in
~/.xemacs, so you might consider
putting it there.
Setting Up Ruby on UNSW CSE Computers
Ruby isn't installed on the CSE computers by default,
however it is installed on ~stulocal,
which is exceptionally easy to set up.
If you use tcsh, firstly, you rock, and secondly, ignore
the instructions on the web site and put this in your .login
file and re-login:
source ~stulocal/bin/setup-env.csh |
If you use bash, put this in your .profile file and
re-login (yes, that's a period at the front):
. ~stulocal/bin/setup-env.sh |
SSDI Lab Solutions in Ruby
I haven't included test cases for these labs, which is
half of the marks. There is a simple reason for this: I
don't have that much time on my hands. ;) There
is a Ruby package called runit
which is a unit testing framework, equivalent to Python's
unittest module. It's used by a lot of Ruby packages for
testing, and if you're really so enthusiastic, I encourage
you to check out some examples.
|