Showing posts with label beginner. Show all posts
Showing posts with label beginner. Show all posts

2008-09-06

class Array

A handful of methods that could be added to the class
Array
:
class Array

def sum
s=0
each{|e| s+=e}
s
end

def mul
m=1
each{|e| m*=e}
m
end

def mean
sum.to_f/length
end

def map_with_index
i=-1
map{|e| yield(e,i+=1)}
end

def map_with_index!
i=-1
map!{|e| yield(e,i+=1)}
end

def any_with_index?
each_with_index{|e,i| return true if yield(e,i)}
false
end

def all_with_index?
each_with_index{|e,i| return false unless yield(e,i)}
true
end

def find_index
each_with_index{|v,i| return i if yield(v)}
end

def find_indices
ret=[]
each_with_index{|v,i| ret<<i if yield(v)}
ret
end

def select_by_index(*indices)
ret=[]
indices.each{|ind| ret<<self[ind]}
ret
end

alias find_indexes find_indices

def to_hash
raise "Cannot convert to Hash!" unless all?\
{ |e|
e.respond_to? :length and e.length==2 and e.respond_to? :[]
}
h={}
each{|e| h[e[0]]=e[1]}
h
end

def keys_to_hash
h={}
each{|e| h[e]=yield(e)}
h
end

def keys_with_index_to_hash
h={}
each_with_index{|e,i| h[e]=yield(e,i)}
h
end

def with(a2)
ensure_same_length(a2)
map_with_index{|e,i| [e,a2[i]]}
end

def with_to_hash(a2)
ensure_same_length(a2)
h={}
each_with_index{|e,i| h[e]=a2[i]}
h
end

def count_all
h={}
each\
{ |e|
h[e]||=0
h[e]+=1
}
h
end

def group_by
h={}
each\
{ |e|
g=yield(e)
h[g]||=[]
h[g]<< e
}
h.map{|g,ee| ee}
end

alias contain? include?
alias has? include?

def rand
a=to_a
a[Kernel.rand(a.length)] unless a.empty?
end

private

def ensure_same_length(arg)
raise ArgumentError,"Argument must be of the same length!"\
unless arg.respond_to? :length and length==arg.length\
and arg.respond_to? :[]
end

end
They are not perfect, but I use them quite a lot.

Some of them (or similar methods) will be present in Ruby 1.9. For example there will be a method
inject
(or
reduce
) working like this:
[1,4,5].reduce(:*)           #=> 20 # 1*4*5
["a","b","dd"].reduce(:+) #=> "abdd"
As you see, they are better than my
sum
, because they work for any type for which the operation is defined. This reduce is not hard to implement, too, but it will probably work a bit faster when included in Ruby core.

Ruby 1.9 is also going to have
group_by
, working exactly like mine, as far as I know.

Move to Enumerable
One more enhancement that can be done in the above code is to move all the methods in the module
Enumerable
(just write
module Enumerable
instead of
class Array
at the top). It allows you to use these methods also with other enumerable types, like
Hash
. You'll have to test the methods, though, as not all of them make sense when used with structures where the elements are not ordered.

Add to load path
If you create some files that you'd like to be easily accessible in your Ruby programs, you can add the path to your files to Ruby load path, so that you will be able to
require
your files without giving the full path. Under Windows, just go to environment variables, and add
RUBYLIB = P:/ath/To/Your/Dir
The path will be automatically added to Ruby load path each time Ruby starts, which can be verified by typing
$:
(or
$LOAD_PATH
) in irb and looking for your path.

If you want some of your files to be loaded even without the need to
require
them, then you can add them to the environment variable RUBYOPT. This variable can already contain -rubygems. If you want the file P:/ath/To/Your/Dir/start.rb to be loaded at startup, change the variable to
-rubygems -rstart
Each word starting with -r makes ruby load a file named by the rest of the word. Ruby will find your file because you already added file path to Ruby load path. If you want to load more files at startup, it is best to
require
them from within your first file.

As you might have guessed, there is file named ubygems that the original content of the variable caused to load. The strange name is in fact chosen only to make the whole command sound reasonable. All it does is load rubygems.rb, which initialises the Gems engine, enabling programs to use additional libraries.

2008-08-24

Object **arr;

What? What do these asterisks do? We're having Ruby here, and the whole old pointers things from C++ are gone, aren't they?

Well, they are not, sorry. Remember one thing, please. This pointers thing is not a part of C++ or any other particular language. Pointers are how computers work. Each (reasonable) programming language either has pointers, or is inefficient. Ruby has them too. The difference is that in Ruby we don't use asterisks to denote we're using them.

What's "wrong"

Have a look at the example that proves we have pointers in Ruby:
a=[2,3,5,7]   #=> [2, 3, 5, 7]
b=a #=> [2, 3, 5, 7]
a<<11 #=> [2, 3, 5, 7, 11]
a #=> [2, 3, 5, 7, 11]
b #=> [2, 3, 5, 7, 11]
So, as you see,
a
and
b
are just pointers. And when we make the substitution in the second line, we just make them point to the same object in memory (array, in our example), so when we modify the object using an in place changing method (like
reverse!
,
clear
and others), the change will be also visible through the other variable.

We observe exactly the same behaviour when we use a string instead of an array:
a="abc"       #=> "abc"
b=a #=> "abc"
a<<"x" #=> "abcx"
a #=> "abcx"
b #=> "abcx"
Note that if you use
+=
instead of
<<
, a new instance of the string with the
"x"
appended is created and assigned to
a
, so if you want to make a string buffer and append to it some lines, it's probably better to use
<<
, because it does not create another object.

So, what if we would like to have an independent copy of a given array or a string? Ruby comes with a function
dup
that makes a copy of an object. (In fact there's also a function
clone
that behaves similarily, for today let's assume the functions do exactly the same (they don't), and let's use
dup
.) Change the line
b=a
to
b=a.dup
in both examples above and you will see it works like expected - modifying
a
does not modify
b
and vice versa.

Happy? So, is that all for today? Not quite. Have a look:
a=["a","b"]
b=a.dup
a<<"c"
b #=> ["a", "b"] # as expected
a[0]<<"x"
a             #=> ["ax", "b", "c"]
b #=> ["ax", "b"] # oops!
What happened? Well, now
b
is a copy of
a
, which means they are two separate arrays, so when we add
"c"
to one of them, the other does not get modified, we already know that. But in Ruby everything is an
Object
, so the elements of the arrays are objects too, and, what worse, they are the same objects. When we did
b=a.dup
, we created a separate array, but the elements of the array are pointers to the same strings as the elements of the original array. Our copy is not deep, we separated the top-level objects but not the elements of the array. So when we modified in place one of the elements, it got modified also in the other array, even though the arrays are separate objects.

Another way to check what happens:
x=Object::new       #=> #<Object:0x2d8ce10>
x.dup #=> #<Object:0x2d91dfc>
[x] #=> [#<Object:0x2d8ce10>]
[x].dup #=> [#<Object:0x2d8ce10>]
As you see,
dup
on an object makes a new object (the addresses differ), but
dup
on an array makes a new array but does not make a copy of the elements - it just makes a new array with its elements pointing to the original objects.

How to "fix" it
OK, so let's change the behaviour of
dup
for
Array
so that it makes a deep copy calling
dup
recursively on all its elements. Good idea? Yes, but NO NO NO!

Never do such a thing as changing the behaviour of a standard function! Someone can depend on how it works now, so you cannot change it! Remember well this lesson! Even if you think it's broken, don't fix it in this way!

Sorry for shouting, but it is important. Let's define a new function with the functionality we've just described. Here's how we do it:
class Object
def deep_dup
dup
end
end

class Array
def deep_dup
map{|e| e.deep_dup}
end
end

class Hash
def deep_dup
h={}
each{|k,v| h[k.deep_dup]=v.deep_dup}
h
end
end
First we define
deep_dup
for "normal" object as a simple copy, as they do not need any special treatment. Then we redefine (override) the function for
Array
and also for
Hash
, as there's exactly the same case with hashes as with arrays. You can check that after changing
dup
to
deep_dup
in the examples above, everything will work as expected.

Of course there are also other classes like
Set
(available after
require 'set'
) that might need overriding
deep_dup
for them to work.

So why use dup?
Now another lesson: why at all use this strange-working
dup
if we have such a nice
deep_dup
? Well, the answer is simple: as
deep_dup
copies everything, it might use much more memory (and time) than
dup
. That's why I said that if a language doesn't have pointers (that is, if a normal substitution works just like our
deep_dup
), it is inefficient. Because the solution is not to stop using
dup
. The solution is to use it carefully and wisely. And to remember, that there are pointers under the nice skin of Ruby.

Other methods
If we need a deep copy, the approach described above is probably the best one, but not the only one. One of the most secure methods to create a complete deep copy of an object is to serialise it to a "soul-less" string and deserialise it back. Then we can be absolutely sure that no part of the new object will be a part of the original one, as deserialisation for sure creates the object from scratch.

Ruby provides two easy serialisation methods:
YAML
and
Marshal
.
require 'yaml'

class Object

def m_dup
Marshal.load(Marshal.dump(self))
end

def y_dup
YAML.load(YAML.dump(self))
end

end
Both
YAML
and
Marshal
have a method
dump
that returns a string representation of the object passed (you can see how the strings look like by calling
dump
on various objects in irb), and the method
load
that does the reverse.

Differences:
- as you can see,
Marshal
's string is shorter so probably it's better and more efficient.
-
YAML
does not always work. I don't know why this is happening, but some complicated structures with sets, arrays, strings and hashes fail to load from the dumped string.

Both methods are most probably worse (slower) than our
deep_dup
, because they need to parse the string.

Last problem - self-references
There's one point, however, where the serialisation method works, and our
deep_dup
fails. It is when an array is an element of itself:
a=[]
a<<a
a #=> [[...]] # the three dots denote a self-reference
a.deep_dup
SystemStackError: stack level too deep
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
... 15577 levels...
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):87:in `deep_dup'
from (irb):87:in `map'
from (irb):87:in `deep_dup'
from (irb):115
from (null):0

Aww, a failure, because duplicating
a
needs duplicating
a
first, and so on. That's why our method is not so perfect, and will fail also on examples like this:
a=[1,2,{:a=>4}]
a[2][:b]=a
a #=> [1, 2, {:a=>4, :b=>[...]}]
It can be fixed and handled just as it is handled in serialisation (it works without problems with such objects), and also in
inspect
(if it wasn't, you'd have an infinite output after creating such an object in irb), but I'll leave this as an exercise for the reader.

One more method
This last method is so bad that you should never use it, I just mention it to give you another knol to think about.
class Object
def i_dup
eval(inspect)
end
end
It calls the method
inspect
to create a human-readable representation of the object, just like irb does after each command, and then passes the string to
eval
that simply executes the string.

You should know by yourself why this method is bad, but just to make it clear:
- it only works for objects "made of"
Array
,
Hash
,
Numeric
,
String
,
Range
and
Symbol
instances (maybe some more I forgot about now), it won't work for
Object::new
,
- it doesn't handle self-reference (it fails when it sees the three dots).

That's all for today, I hope you learnt something new.

2008-08-15

rescue

About exceptions today. First - if you're new to exceptions, probably 'exception' means to you 'an ugly error message, full of Access denied, memory under 0x462F3ED4 cannot be read stuff, contact the sucker who sold you this product.' Time to change this way of thinking!

Why the hell they invented exceptions
Imagine a complex system, let's say, a system to operate some machine in a factory. The system is divided into layers, that is (for example, in order) direct motor control, motor control proxy, motor controller, control logic, user commands layer, several other layers, the machine operation layer, several more layers, and finally the user interface, where user wants the machine to do something.

So you can imagine, that when user presses a button, then a function from user interface layer calls a function from the next layer, that calls a function from the machine operation logic layer, that... that finally sends a signal to the inverter to run the motor. And now imagine that the motor is completely broken, burnt, stalled or stolen. It's a critical error, so user must be immediately informed about this, and no further action of any of the layers is needed.

The naïve solution: each of the functions should return an integer with error code, and each function, when calling the next one in the chain, should check its return value, and if it signalises an error, it should return immediately with the same error code, until the last function (the first that was called) gets the message. It's a good solution, meaning that it can work. But it's a very bad solution:
- The code becomes ugly and long.
- Functions cannot return any other value because they already return the error code, and it complicates even further.
- If a function has to do something no matter if it succeeded or not (like close a file or release resources), and it complicates even further than further.
- The code becomes ugly and long.
- The code becomes ugly and long.
Have a look at an example:
def prepare()
if motor_in_bad_mood?
return 225 # the error code for the problem
end
inverter.init()
return 0
end

def do_it()
if ufo_stole_the_cables?
return 843 # the error code
end
inverter.operate()
# further operation
return 0
end

def almost_do_it()
f=allocate_resources()
if (ret=prepare())!=0
f.free_resources()
return ret
end
if (ret=do_it())!=0
f.free_resources()
return ret
end
f.free_resources()
return 0
end

def user_says_do_it()
if (ret=almost_do_it())!=0
puts "The operation returned the error code! (#{ret})"
end
end
If you're not completely blind, you see the ugliness of this code. (Of course in Ruby we could do some improvements, but imagine it is C. And remember there is a lot more functions that call each the next one.)

Any idea for a solution? Well, the best one is, if the most inner function could inform the most outer function that something's completely wrong. But how to do it? And what if we don't want to inform the most outer function, for example because the problem is not so critical, and can be resolved by the program?

Let's finally present the solution with exceptions, or
Exceptions
, as we should call them now.
class CriticalMotorError < RuntimeError
end

def prepare()
if motor_in_bad_mood?
raise CriticalMotorError,225
end
inverter.init()
end

def do_it()
if ufo_stole_the_cables?
raise CriticalMotorError,843
end
inverter.operate()
# further operation
end

def almost_do_it()
f=allocate_resources()
begin
prepare()
do_it()
ensure
f.free_resources()
end
end

def user_says_do_it()
begin
almost_do_it()
rescue CriticalMotorError => e
puts "The operation returned the error code! (#{e.message})"
rescue RuntimeException => e
puts "Something even worse happened: #{e.message}."
end
end
Notice any differences? Let's explain what happened. First, we declared our error class, descendant of the standard error class in Ruby,
RuntimeError
(which is a descendant of
Exception
used for most error that happen during program runtime). Our exception class doesn't do anything special, it just inherits from the ancestors.

Now, when an error occurs, we
raise
an exception (in other languages the keyword throw is often used here). That means that we create a new instance of our exception, pass it an argument (exception simply stores it as an error message and does nothing with it), and then
raise
thus created exception. Raising means that all further actions in the current function are aborted, and the control returns to the higher function, but here also all actions are aborted, and the function exits immediately, and all functions in the chain exit in a row. If we just threw an exception and then didn't take care of it, it would exit all the functions, and finally also exit the program with an error message (try to type this in irb:
def x;0/0;end;def y;x;end;def z;y;end;z
to see this behaviour in action).

But here we don't want the program to exit. So we make a trap. A trap is the
begin
and
end
in
user_says_do_it
, and the
rescue
. Basically, if things that are called after
begin
throw an exception, and the exception is mentioned in one of the
rescue
clauses, then the rest of the block after
begin
is skipped and the control goes to the right
rescue
clause (the first that mentions the actual class of the exception), and then resumes after
end
, and continues to run the program normally (the exception is cancelled once the control enters the
rescue
clause, and the exit-immediately madness stops).

What we do in the
rescue
clause, we print an error message, including the message (error code) that we read from within the object
e
which is our exception. Simple, elegant, painless.

As you see, there's one more trap, inside
almost_do_it
. It also detects exceptions raised from the block, but it doesn't rescue them, it just isn't interested in what really happened, or it decides it doesn't have power to serve any errors correctly, so it just lets the exceptions pass through, also skipping the rest of the block (so if the exception was raised by
prepare
,
do_it
won't even try to execute. But here's the trick: the
ensure
block gets executed no matter what happened inside the
begin
block. It executes both when the block completes normally, and when it is interrupted by an exception, but
ensure
doesn't cancel the exception, it just stops for a moment to do what it has to do, and goes on with the unrolling madness.

As you see, the exceptions are very useful, simple, elegant, powerful and in general good. Use them!! Learn them, use them, think about them, or else you're not a programmer for me.

Final remarks about exceptions
The two following examples are equivalent:
begin
try_something()
puts "Success." # of course this line is executed only if no exception was raised
rescue SomeError => e
error()
# possibly more rescue clauses
end
And the second, looks a bit nicer for me:
begin
try_something()
rescue SomeError => e
error()
# possibly more rescue clauses
else
puts "Success."
end

Another remark.
begin
# ...
rescue RuntimeError
# ...
rescue CriticalMotorError
# ...
end
This is useless, because our
CriticalMotorError
is also a
RuntimeError
, so the first
rescue
will be triggered, and always when a
rescue
is triggered, all following
rescue
s are skipped and not even checked.

Also, as you see, the
=> exception_var
can be omitted. The exception class name can be omitted also, and it defaults to
StandardError
. For standard exception classes, check QuickRef, the part Exceptions, Catch, and Throw (
catch
and
throw
are not so good tools, though).

The last remark. If a function in the calling chain thinks it cannot serve the exception, but thinks that it could add some additional info to the error, it can
rescue
it and either throw a new error with some more data (possibly of some other class than the original exception), or it can do something like this:
begin
# ...
rescue SomeException => e
puts "The exception was rescued: #{e.message}"
puts some_additional_info
raise e
end
In this way, the exception gets partially served, and then reraised so that it does not get cancelled at this point.

Use it, use it, use it!

2008-08-14

Downloading a file

Today we will download a Garfield comic strip. We will not display it, as displaying is a whole lot harder, it needs a window and so on, maybe I'll cover this subject one day, but for now just download it to your hard drive.

Let's start with code and then follow with step-by-step explanation.
require 'date'
require 'open-uri'

GARFIELD_START=Date::new(1978,6,19)

puts "When were you born? (YYYY MM DD please)"
print "?> "
t_date=gets.strip.split(/[^0-9]+/).reject{|e| e.empty?}.map{|e| e.to_i}
if t_date.length!=3
puts "YYYY MM DD, I said!"
exit
end
date=Date::new(*t_date)
if date<GARFIELD_START
puts "You are older than Garfield, so no comic strip for your Birthday."
exit
end
remote=date.strftime("http://images.ucomics.com/comics/ga/%Y/ga%y%m%d.gif")
local=date.strftime("C:/Garfield %Y-%m-%d.gif")
data=open(remote).read
File::open(local,"wb"){|f| f<<data}
puts "Comic strip for your Birthday downloaded."
First, we need two additional libraries, so we
require
them. The first one adds a lot of functionality to the class
Data
(for manipulating time, if you have not guessed that), and the other makes it possible to download files from the internet very easily (of course there are also ther ways to do it, like to issue a regular HTTP connection and so on, but leave it for another day).

GARFIELD_START=Date::new(1978,6,19)
- we define a constant (in Ruby, if the first letter of a variable name is a capital leter, then it's a constant). This is the first day for which Garfield is available on web. The constructor takes year, month and day.

Now we print a question and a prompt. And now the next line:
t_date=gets.strip.split(/[^0-9]+/).reject{|e| e.empty?}.map{|e| e.to_i}
first we call
gets
- it reads a line from standard input, that is, from console. Then we do some magic with it, and why we do it is that we want to make it possible to enter 2000 01 01 as well as 2000-01-01 or 2000/01/01 or bwah2000----01??01yeah. We want to be flexible.

So first we call
strip
to strip what user has entered of white characters at the beginning and the end (this is not really necessary here but let's do it anyway). Now we want to get from the string all digit groups. As you should already know from some previous post, this should work:
.scan(/[0-9]+/)
, but here I wanted to use another (worse) way to do it, to teach you something new. So we do not scan the string for groups of digits, we split the string by groups of non-digits instead. That means that all groups of non-digits become separators and are left out, and what was between them is returned in an array.

To test how exactly this works, simply enter something like
"bwah2000----01??01yeah".split(/[^0-9]+/)
in irb. You will notice that it works, the only problem is that the returned Array has one more element than we wanted:
["", "2000", "01", "01"]
(that's why
split
is worse than
scan
here). This is, of course, because the string began with a non-digit group, and when it became separator, what was before it became an element.

And that's why we call
.reject{|e| e.empty?}
now. What it does? It executes the block once for each element of the array, but it not only executes it, but also checks what the block returned. The block returns
true
for empty elements, and
false
for other. The method
reject
, as the name says, rejects from the array these elements, for which the return value of the block was
true
. So this will simply delete the empty elements, in our case only the first element can be empty. You can apped the call to theis function to your irb line to check it.

So finally we have three number (for correct input), but they are still not numbers. You see? They are in quotes, they are parts of the input string so they are Strings. So we want to convert them all to Integers. We do it with the last element in the chain:
.map{|e| e.to_i}
. This function again calls the block with each element in turn, and it exchanges each element in the array with what the block returned for this element. Best if you call it in irb to see.

Now we check if we finally have 3 numbers.
exit
exits the whole program.

date=Date::new(*t_date)
- here we create the
Date
element for the specified date. The asterisk before the argument is the splash operator and it makes that our array of 3 elements is not passed as Array, but as 3 separate arguments for the function.

The dates comparison does not need explanation.

Now we use
strftime
to create strings that have parts of the date in them. Best check the results in the console.

Now we do what the included file
'open-uri'
allowed us to do - we open a remote file simply by calling
open(url)
, and read data from the file. All in one line! The data is stored in a vriable as String, but here String means just that it is a string of bytes, and not something readable.

After that, we open the local file on your hard drive. We open the file with the second argument
"wb"
to denote that we want only to write to the file (and overwrite it, if already exists), and that the data we want to operate on is binary. This is very important! If you do not specify binary data and write or read binary data, something will go wrong, almost always. Remember.

Now, how do we use files. We could do it like this:
file=File::open(name,mode)
# operations on file
file.close()
But then we have to remember to close the file, especially if we write to i, or else the data won't get flushed to disk. But we can also pass a block to
File::open
, and then the method doesn't return the file, instead it calls our block and passes the newly opened file object to it, and after the block finishes, it closes the file gracefully, so that we do not have to do this. This is a good way to write to files, more elegant and safer. (Note that here the block gets executed only once. Do not associate a block with a loop, it's the called function that decides what to do with the passed block, and this thing that
File
does it is also a common behaviour.)
File::open(name,mode)\
{ |file|
# operations on file
}
So inside the block in our program, the variable
f
is the opened local file. Now we just write the data to it (
<<
is the same as
write()
), and finish the program. Check that it works!

One question might arise, why didn't I just write
f<<open(remote).read
. Well, if you had some connection error so that
open
would fail and interrupt the program, you would already have an empty file on your hard drive, and it would remain there and you would have to remove it manually (or overwrite by running program again, successfully). But when you first read data and only then open the file, then in case of error, the file opening line doesn't even get executed, and the file is not created.

2008-08-11

The second step

Real programs
Time to write another post, probably. Now that you have already your console set up, I will tell you (or maybe you already know it?) how to write (sort of) real programs - Ruby programs that you can run by clicking their icon or entering their names.
It's very simple: just open SciTE - the text editor that came with the Ruby installation, write a program (no int main(){ ... return 0;}, just write your code just like in the console), and save it as an .rb file anywhere on your hard drive. Then go to this directory and double-click the file. If your Ruby is installed properly, it should run in a new console window, just as .bat or other batch files do. (If you see just a blink of a console, add
gets
command at the end of your program so that it waits for Enter.) If it doesn't work then you can instruct your system to open this kind of file with ruby.exe which is located in your C:\Ruby\Ruby\bin directory (if you followed my advise from the previous post).

I'd suggest you to put your programs under C:\Ruby\Programs, each program in a separate directory, even if they consist of just one .rb file, as your first programs probably do. It helps keeping things tidy later.

Hmm, what to write now?
OK, why I'm talking all around the language, but have not given you a slightest hint I really know how to write a Ruby program? OK, let's have a look at a code snippet here:

"Ruby - Al2O3::Cr".scan(/./).each_with_index{|c,i| puts "%2d: %s (%3d)"%[i,c,c[0]]}

Do you know what it does, at a first glance? If no, then note my words now: in a moment you're going to gain a nice piece of knowledge. The Ruby learning curve is very pleasant - things look terribly complicated until you finally get to them and they turn out to be very easy and nice. Let's analyse the line, function by function.

"Ruby - Al2O3::Cr"
- we create a String object containing exactly what you see. A bonus knol about Strings: to have a quote mark in a String, prepend it with backslash, like this:
"This is quoted: \"abc\"."
. To get a backslash, write a double backslash. To get a line end, write
"\n"
.

aString.scan(/./)
- here we call the method scan of a string. This method takes a regular expression and returns an Array of all parts of the string that match the regular expression. Regular expressions in Ruby are enclosed within slashes. A dot means any single character. So - finally - the result of the function will be an Array, each element of which will be a single character from the string. If you're not sure if you understand, write
"Ruby - Al2O3::Cr".scan(/./)
in irb and everything will become clear. So now we have an Array of single characters.

anArray.each_with_index
- this method takes a closure - the thing in the curly brackets, and does with it what the method name says: calls it with each element and its index. So if your closure looks like the one in the example above, then it will be called once for each element in the array (total of 15 times here), and each time the variables
c
and
i
inside the closure will have different values: first time
c="R"
and
i=0
, second time
c="u"
and
i=1
, third time
c="b"
and
i=2
... got it? I hope so.

Inside the closure we execute this:
puts "%2d: %s (%3d)"%[i,c,c[0]]
. (It means that the puts command will be executed 15 times, as already said). And the argument to the command is
"%2d: %s (%3d)"%[i,c,c[0]]
, which might look a bit complicated at first, but which is not complicated at all. Just type in irb something like
"a%db"%5
and see what comes out. Now try
"%3d"%5
, or
"%03d"%5
, or
"%.2f"%5.6
. Now you can try with more % fields in the string:
"-%d-%s-"%[15,"xx"]
. As you see, all that changes is that now you have to pass an Array of values to insert into % fields. So please, remember this useful operation:
aString%anArray
makes more or less the same as the old sprintf, only nicer!

Back to our formula, we output here three arguments, and Integer (%d), a String (%s) and another Integer, for which we pass
c[0]
. This might need a bit of your attention, as it's a small gotcha in Ruby: the square bracket on a String does not extract a character from the given index, it extract the code of the character. So that's the easiest way to convert the variable c (which is a single character, as you surely remember) into its ASCII code. Note that this is going to change in Ruby 1.9!

OK, that's it. Even if you feel like you haven't learnt a lot today, please stay tuned for the next part, which is going to be a bit more interesting! We're going to tackle classes.

If you have some free time and want to learn something by yourself, register on DonationCoder and go to Ruby Programming School. It's a nice place to prove you've learnt something today!

2008-08-10

Let's start

OK, all blog options are set up like they should, page layout is chosen, colours are adjusted. Probably it's time to write the first post here. As you might have guessed, this blog is going to be about Ruby - an interesting, inspiring, very easy and very complicated at the same time, programming language. That sentence should make it as an introduction, and let's close this topic for now.

If you are a beginner, then that's the best place for you to start. I'll try to gather some interesting beginner tutorials and useful info, and also some of my knowledge. I will also post on where to download and how to install Ruby and some useful modules. I hope that will help you a bit at the beginning.

If you are an expert... well, I'm and expert too. I guess if you are here, then your knowledge lacks some parts, as usually experts' knowledge does, not excluding my own. If what I know fits what you don't, I'll be glad to have helped you. And I'm sure I can, here or there. The problem for now is that I have to start off with the whole bunch of beginner things, and then I will get back to what's really fascinating about Ruby.

Tutorials
So let's tackle some real problem. How to start with Ruby? First, please try it out, if you haven't done it yet: A Hands-on Ruby Tutorial! It's really cool to start with it - you don't have to install anything, and you have access to the real programming and not some "now imagine that we do this and get this." Start with typing 'help' and then just follow the tutorial.

You might also want to try this (non-interactive) tutorial about "object-orientedness" of Ruby: Tutorial.

Get a console of your own
Once you see (I'm sure you do!) that there's something in it, I have great news for you! You can have a console just like the one on the webpage, only better, right on your computer! Do you think I'm using Window's calc anymore? Believe me, I don't. So: if you are using Windows, go here and download the last stable release (at the time of writing it is ruby186-26.exe). Install it, but choose carefully the path! I suggest installing it in something like C:\Ruby\Ruby\, and that's because you might want to store your programs in C:\Ruby\Programs\, your gems (about these later on) in C:\Ruby\Gems\, etc. But as you prefer. But remember, never put your own files in the installation directory. They might not survive a Ruby version upgrade. During the installation, always agree to the default options, they are reasonable.

Once you finished the installation, let's get to the Ruby console I promised you. Open the Windows console (Start | Run, cmd, Enter) and type irb, Enter. If this appears: irb(main):001:0> then here you are, the console is ready! If an error occurs instead, you will have to add the Ruby binaries path to your environment path variable. Right-click My computer on your desktop, Properties | Advanced | Environment variables, locate Path on the bottom list, Edit, and add to it ;C:\Ruby\Ruby\bin or something else if you installed Ruby elsewhere. Accept changes, close the console, open it again and try to run irb, it should work now.

If you want to have quick access to your irb, right-click on the desktop, New | Shortcut, and as the element to run type %windir%\system32\cmd.exe /k irb.bat and accept. (It is better to do it like this and not just link to the irb because if you happen to get an error that closes irb, you can still read the error message because the window won't close. What could cause such a terrible error that closes irb? Oh, if you must know, type
STDOUT.close
)

Now I'll leave you with your new toy. Play with it and explore. Here's a nice spec of basic Ruby objects like Arrays and Strings, have a look: Ruby Class Reference. Another tutorial: Why's... but... be careful with this one...

In the next post we will start up the rubygems which you are likely to need later. In following posts, I will present interesting classes, modules and functionalities, both in the standard Ruby distribution, and in the additional modules. That's all for today, see you!