Η γλώσσα προγραμματισμού Ruby
© Γιάννης Κωστάρας
<- | -> |
Η Ruby είναι μια ακόμα δημοφιλής γλώσσα προγραμματισμού, που έρχεται μαζί με τη διανομή Ubuntu. Πρόκειται για μια διερμηνευτική (interpreted) γλώσσα προγραμματισμού ανοικτού κώδικα, η οποία μπορεί να χρησιμοποιηθεί για πολλές κατηγορίες προγραμματισμού (multi-paradigm).
Δημιουργήθηκε από τον Yukihiro “Matz” Matsumoto το 1993. Η 1η έκδοση βγήκε το 1995, έχει δηλ. ηλικία όσο και η Java. Ο συγγραφέας της προσπάθησε να πάρει τα καλύτερα στοιχεία από τις πιο διαδεδομένες γλώσσες προγραμματισμού, όπως οι Perl, Python, Smalltalk, Java, C, C++ κ.ά. Είναι, όπως θα δούμε, μια απ’ τις πιο εκφραστικές και συνοπτικές γλώσσες προγραμματισμού. Συνοπτικά η Ruby είναι:
- Ανοικτού κώδικα (Open Source)
- Multi-paradigm, δηλ. μπορεί να χρησιμοποιηθεί με πολλά μοντέλα προγραμματισμού, όπως συναρτησιακά (functional/procedural) ̶ βλέπε C, Pascal, Fortran ̶ αντικειμενοστραφή (object oriented) ̶ βλέπε Java, C++, C#,Smalltalk κ.ά.
- Interpreted, ή διερμηνευτική, δηλ. το πηγαίο πρόγραμμα εκτελείται ως έχει (όπως π.χ. στη Python ή στη Perl), σε αντίθεση με τις compiled ή μεταγλωτιστικές γλώσσες όπου το πηγαίο πρόγραμμα μεταγλωτίζεται πρώτα και παράγεται το αρχείο προς εκτέλεση (π.χ. Fortran, C/C++/C#, Pascal, Java).
Πολύ διαδεδομένη είναι επίσης και η γλώσσα προγραμματισμού Ruby on Rails για ανάπτυξη διαδικτυακών εφαρμογών. Αν και ως διερμηνευτική γλώσσα, είναι πάντα πιο αργή κατά την εκτέλεσή της από τις μεταγλωτιστικές γλώσσες προγραμματισμού, όπως η Java και η C, ο χρόνος δημιουργίας και εκτέλεσης προγραμμάτων είναι πολύ πιο άμεσος και γρήγορος, καθώς δεν υπάρχει το ενδιάμεσο βήμα παραγωγής του εκτελέσιμου αρχείου.
Εγκατάσταση της Ruby
Εκδόσεις της Ruby διατίθενται για όλες τις πλατφόρμες των Windows, Unix, Linux και MacOS. Μπορείτε να κατεβάσετε το κατάλληλο περιβάλλον ανάπτυξης για τον λειτουργικό σας σύστημα από το http://www.ruby-lang.org/en/downloads/. Πλέον η Ruby έρχεται προεγκατεστημένη σε όλα τα Unix-οειδή περιβάλλοντα και φυσικά και στο Ubuntu. Για να το διαπιστώσετε, δώστε από το κέλυφος την εντολή:
$ ruby –version
ή
$ ruby -v
Θα πρέπει να δείτε κάτι σαν το παρακάτω:
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
Αν όχι, τότε μπορείτε να την εγκαταστήσετε δίνοντας την εντολή:
% sudo apt-get install ruby irb rdoc fxri
Περιβάλλοντα ανάπτυξης
Υπάρχουν πολλά περιβάλλοντα ανάπτυξης εφαρμογών Ruby, όπως:
- Aptana
- Jetbrains RubyMine
- Netbeans
- FreeRide
- TextMate για MacOS
- TryRuby όπου μπορείτε να δοκιμάσετε τη γλώσσα χωρίς να χρειαστεί να την εγκαταστήσετε τοπικά στον Η/Υ σας.
Το πρώτο σας πρόγραμμα
Αν εγκαταστήσατε τη Ruby, τότε ανοίξτε ένα παράθυρο κελύφους (shell) και πληκτρολογείστε:
$ irb
που σημαίνει ‘interactive ruby shell’. Αν εγκαταστήσατε και το fxri
που σας παρέχει και on-line βοήθεια, μπορείτε να το εκκινήσετε δίνοντας την εντολή fxri
αντί για την irb
. Πληκτρολογήστε την παρακάτω εντολή:
irb(main):001:0> puts "Hello world!"
και πατήστε Enter
.
Hello world!
=> nil
Παρατηρήστε πόσο άμεσο ήταν το αποτέλεσμα χωρίς να περιμένουμε να μεταγλωτιστεί το πρόγραμμα. Η εντολή puts
εκτυπώνει στη μονάδα εξόδου, δηλ. την οθόνη. Αντίστοιχα, υπάρχει και η εντολή gets
η οποία διαβάζει από τη μονάδα εισόδου, δηλ. το πληκτρολόγιο.
Πληκτρολογήστε exit
για να βγείτε από το περιβάλλον εργασίας της Ruby. Ανοίξτε τον αγαπημένο σας επεξεργαστή κειμένου και πληκτρολογήστε το ακόλουθο πρόγραμμα:
puts "Hello. What is your name?"
myname = gets()
puts "Well, hello there #{myname}"
Αποθηκεύστε το αρχείο με όνομα Hello.rb
. Στη γραμμή εντολών, μεταφερθείτε στον κατάλογο που αποθηκεύσατε το παραπάνω αρχείο και πληκτρολογήστε:
$ ruby Hello.rb
Hello. What is your name?
John
Well, hello there John
Μπορείτε επίσης να εκτελέσετε το αρχείο δίνοντας απλά την εντολή
$ ./Hello.rb
αν προσθέσετε την ακόλουθη γραμμή στην αρχή του προγράμματός σας, δηλ.:
#!/usr/bin/ruby
puts "Hello. What is your name?"
myname = gets()
puts "Well, hello there #{myname}"
Σύγκριση με άλλες γλώσσες προγραμματισμού
Οι αρετές της Ruby έρχονται στην επιφάνεια όταν πρόκειται να τη συγκρίνετε με άλλες γλώσσες προγραμματισμού. Π.χ. ένα πρόγραμμα Ruby που δεν κάνει τίποτα, είναι απλά … ένα κενό αρχείο ή ένα πρόγραμμα χωρίς καμία εντολή! Αντίθετα, π.χ. στη Java, ένα πρόγραμμα που δεν κάνει τίποτα πρέπει να ‘χει τις ακόλουθες εντολές:
class Empty {
public static void main (String args[]) { }
}
Τύποι και δομές δεδομένων
Η Ruby διαθέτει ένα πλούσιο ρεπερτόριο τύπων και δομών δεδομένων. Διαθέτει τους απλούς τύπους δεδομένων String
και Number
(FixNum, BigNum, Float
) καθώς και συλλογές όπως Range, Array
και Hash
. Στη Ruby τα πάντα είναι αντικείμενα, ακόμα και οι αριθμοί. Όλα κληρονομούν από την κλάση Object
.
1.class → FixNum
0.5.class → Float
Στη Ruby δεν δηλώνουμε τον τύπο δεδομένων μιας μεταβλητής, όπως π.χ. στη C. Οι τύποι δεδομένων στη Ruby είναι δυναμικοί, αντίθετα με τους στατικούς τύπους δεδομένων γλωσσών όπως οι C, Pascal, Java κλπ. Έτσι, π.χ. ένας ακέραιος στη Ruby ορίζεται π.χ. ως:
myInteger = 0
κι ένα αλφαριθμητικό ως:
s = 'This is a String'
Υπάρχουν δυο τύποι δεδομένων για την αναπαράσταση αριθμών στη Ruby: Fixnum (-2-30 – 230-1)
και BigNum
(εκτός των παραπάνω ορίων). Η μετατροπή γίνεται αυτόματα.
Ο τύπος δεδομένων Σειρά ή Διάστημα (Range) αποθηκεύει μια σειριακή ακολουθία τιμών, όπως π.χ. 1
ως 9
ή Α
ως Ω
. Μια σειρά ορίζεται από την τιμή αρχής, τα ..
ή ...
και την τιμή τέλους. Π.χ.
capital_letters = 'A'..'D' → ["A","B","C","D"]
numbers = 0..9 → [0,1,2,3,4,5,6,7,8,9]
years_of_birth = 1970...1973 → [1970,1971,1972]
Παρατηρήστε ότι στο τελευταίο παράδειγμα (...
), το διάστημα είναι ανοικτό, δηλ. δεν περιλαμβάνει την τιμή τέλους. Παραδείγματα πινάκων και κατακερματισμένων πινάκων (hashtables) φαίνονται παρακάτω:
empty_array = []
empty_array = Array.new
int_array = [1,2,3,4,5,6]
random_array = [1, 'Hi', 3.14]
two_dimensional_array = [[1,2],[2,1]]
myarray = (1..9).to_a # μετατροπή σε array
my_hash = { 'one' => 1, 'two' => 2 }
my_hash['three'] = 3
Διαδικαστικός/Συναρτησιακός προγραμματισμός
Όπως είπαμε, η Ruby μπορεί να χρησιμοποιηθεί με πολλά προγραμματιστικά στυλ, όπως ο διαδικαστικός (procedural) / συναρτησιακός (functional). Μ’ αυτόν τον τρόπο, μπορεί κάλλιστα ν’ αντικαταστήσει παραδοσιακές διαδικαστικές γλώσσες όπως οι: Pascal, C, Fortran, Basic.
Ένα μπλοκ εντολών στη Ruby δηλώνεται με τις εντολές begin ... end
.
Υπάρχει εντολή if ... elsif ... end
, ενώ η εντολή case
συντάσσεται ως εξής (το ARGV
είναι ένα array με τα ορίσματα γραμμής εντολής):
case ARGV.length
when 0
puts "usage #{$0} arg1 arg2"
when 1
puts "Hello #{ARGV[0]}"
when 2
puts "Hello #{ARGV[0]} #{ARGV[1]}"
else
puts "Tοo many arguments"
end
Παρατηρήστε ότι δεν απαιτείται η χρήση εντολής break
στο τέλος κάθε when
. Η case στη Ruby είναι αρκετά ευέλικτη σε σχέση με τις Java, C, C++, C#. Κοιτάξτε κι αυτό:
case a
when Integer
puts "Type: Integer "
when String
puts "Type: String"
else
puts "Type: #{a.class.to_s}"
end
Υπάρχει κι εδώ ο τριαδικός τελεστής ?
(ARGV.length>0) ? puts "Hello " + ARGV[0] : puts "Usage #{$0} name"
ενώ η εντολή if
μπορεί να μπει και στο τέλος της πρότασης
puts "Hello " + ARGV[0] if (ARGV.length>0)
Η Ruby διαφέρει όμως αρκετά όσον αφορά τους βρόγχους, από τις γλώσσες C, C++, C#, Java. Ακολουθεί τη σύνταξη της Smalltak κι αυτό ίσως ξενίσει μερικούς. Όπως θα δούμε όμως, έχει το πλεονέκτημα ότι δεν χρειάζεται να δηλώσετε μετρητή.
Ας ξεκινήσουμε με το βρόγχο for
for arg in ARGV
print " #{arg}"
end
το οποίο μπορεί να γραφεί κι ως:
print "Hello"
ARGV.each {
|arg| print "#{arg}"
}
Ο πιο πάνω κώδικας χρησιμοποιεί closures, δηλ. μπορείτε να περάσετε ένα μπλοκ κώδικα ως όρισμα σε μια μέθοδο!
Ο βρόγχος for
μπορεί να γραφτεί και με τους παρακάτω τρόπους:
# CountToTen.rb -- Count to ten
(1..10).each do
|i| print " #{i}"
end
puts 'All done!'
το οποίο μπορεί να γραφτεί και διαφορετικά ως:
# CountToTen.rb -- Count to ten
for i in (1..10)
print " #{i}"
end
puts 'All done!'
ή ακόμα και ως:
1.upto(10) {
|i| print " #{i}"
}
puts 'All done!'
Φυσικά υπάρχουν και οι βρόγχοι while
και until
:
x = 0
while (x < 10)
print x.to_s
x += 1
end
Το αποτέλεσμα είναι: 0123456789
.
Ο παραπάνω βρόγχος μπορεί να γραφτεί κι ως:
x = 0
until (x >= 10)
print x.to_s
x += 1
end
ή και ως
x = 0
begin
print x.to_s
x += 1
end until (x >= 10)
Παρατηρήστε ότι δεν υπάρχουν οι τελεστές ++
και --
. Αντί αυτών χρησιμοποιήστε τους +=1
και -=1
.
H Ruby διαθέτει επίσης και τις εντολές break
και next
, που είναι αντίστοιχες με τις break
και continue
στις Java/C. Η πρώτη σταματάει το βρόγχο και συνεχίζει με την επόμενη εντολή που ακολουθεί το βρόγχο, ενώ η δεύτερη αγνοεί τον κώδικα που ακολουθεί μέχρι το τέλος του βρόγχου και συνεχίζει στην επόμενη επανάληψη.
Αντικειμενοστρεφής προγραμματισμός
Όπως είπαμε, τα πάντα στη Ruby είναι κλάσεις. Όπως και σ’ άλλες αντικειμενοστρεφής γλώσσες προγραμματισμού (όπως C++, C#, Java, Python), έτσι κι η Ruby σας επιτρέπει να δημιουργήσετε αντικείμενα από κλάσεις που εσείς ορίζετε. Αντίθετα όμως με strong ή static typed γλώσσες όπως οι C#, C++ και Java, η Ruby, ως dynamic typed δεν απαιτεί να δηλώνεται ο τύπος κάθε αντικειμένου. Όπως και στη Java και τη C#, έτσι και στη Ruby, όλες οι κλάσεις κληρονομούν απ’ την κλάση Object. Τέλος, όπως και η Java και η C#, η Ruby δεν υποστηρίζει πολλαπλή κληρονομικότητα. Ορίζουμε μια κλάση στη Ruby όπως φαίνεται παρακάτω:
# car.rb – class definition
class Car
# constructor
def initialize(color, speed)
@color = color # attribute
@speed = speed
self.info
end
# method
def accelerate(speed)
@speed += speed
self.info
end
# another method
def info()
puts "Car info (color: #{@color}, speed: #{@speed.to_s})"
end
end
Οι ιδιότητες (attributes) δηλώνονται με το @
και οι μέθοδοι με τη δεσμευμένη λέξη def
. Η μέθοδος κατασκευής (constructor) ονομάζεται initialize
.
>> car = Car.new('black', 0)
Car info (color: black, speed: 0)
>> car.accelerate(10)
Car info (color: black, speed: 10)
Ένα χαρακτηριστικό της Ruby, που μερικές φορές μπορεί να αποβεί κι άκρως επικίνδυνο, είναι ότι μπορούμε να επεκτείνουμε μια υπάρχουσα κλάση, χωρίς να χρειάζεται να παραθέσουμε ολόκληρο τον κώδικά της, π.χ.
class String
def writesize
puts self.size
end
end
>> size_writer = "Tell me my size!"
>> size_writer.writesize → 16
Στο πιο πάνω παράδειγμα είδαμε ότι επεκτείναμε την κλάση String της Ruby προσθέτοντας μια νέα μέθοδο writesize
.
Οι ιδιότητες της κλάσης Car
, που ορίσαμε παραπάνω, δεν είναι προσπελάσιμες εκτός της κλάσης. Ο παρακάτω κώδικας δημιουργεί μεθόδους get
και set
για τις ιδιότητες της κλάσης Car
:
# car.rb – class definition
class Car
#getColour
def colour
@colour
end
#setColour
def colour=(colour)
@colour=colour
end
>> car.colour='white'
>> puts car.colour
white
Για να διαβάσουμε μια ιδιότητα, αρκεί να ορίσουμε μια μέθοδο με τ’ όνομά της που να περιέχει την ιδιότητα (η τιμή της τελευταίας εντολής μιας μεθόδου είναι αυτή που η μέθοδος επιστρέφει). Για να θέσετε/αλλάξετε την τιμή μιας ιδιότητας, δηλώνετε μια μέθοδο με τ’ όνομά της που ν’ ακολουθείται από το =
και το όνομα ενός ή περισσοτέρων ορισμάτων.
Η Ruby διαθέτει κι έναν πιο εύκολο τρόπο να ορίσουμε αυτές τις μεθόδους, όπως φαίνεται στα παρακάτω παραδείγματα:
# car.rb – class definition
class Car
attr_reader :color, :speed
attr_writer :color, :speed
end
ή
class Car
attr_accessor :color, :speed
end
Η παραπάνω δήλωση, ορίζει μεθόδους get/set
ή αλλιώς read/write για τις ιδιότητες της κλάσης. Βέβαια, έτσι χάνουμε την ευελιξία να προσαρμόσουμε τις μεθόδους αυτές αν χρειάζεται π.χ. να ελέγξουμε αν οι τιμές των ορισμάτων είναι έγκυρες.
Όλες οι μέθοδοι που ορίσαμε πιο πάνω είναι public
, δηλ. προσβάσιμες από οποιαδήποτε άλλη κλάση ή μπλοκ κώδικα. Αν θέλουμε να περιορίσουμε ποιος μπορεί να προσπελάσει τις μεθόδους της κλάσης μας, χρησιμοποιούμε τις δεσμευμένες λέξεις private
και protected
:
# car.rb – class definition
class Car
private
def testDrive
end
protected
attr_accessor :category
public
attr_accessor :color, :speed
end
Οι ιδιότητες και οι μέθοδοι που δηλώνονται private
είναι προσβάσιμες μόνο από το μπλοκ της κλάσης, ενώ αυτές που δηλώνονται protected
είναι προσβάσιμες και από τις κλάσεις παιδιά δηλ. που κληρονομούν απ’ αυτήν την κλάση, όπως π.χ. την ακόλουθη:
# SportCar.rb – class definition
class SportCar < Car
def initialize(seats)
@seats = seats
end
end
Η κληρονομικότητα δηλώνεται με το <
και την πατρική κλάση.
Προσέξτε ότι οι λέξεις private
και protected
δηλώνουν με την ιδιότητά τους τις γραμμές κώδικα που ακολουθούν, μέχρι ν’ συναντήσουν κάποια άλλη δεσμευμένη λέξη, όπως π.χ. public
.
Οι στατικές ιδιότητες δηλώνονται με το @@
ενώ οι στατικές μέθοδοι έχουν ως πρόθεμα το όνομα της κλάσης χωρισμένο με την τελεία (.
):
class Car
NUM_OF_WHEELS = 4 # constant
@@wheels = NUM_OF_WHEELS
def Car.static_method()
end
end
Δομές και Ενότητες
Η Ruby υποστηρίζει δομές όπως οι C, C++. Μια δομή (structure) είναι ένας απλός τρόπος να ομαδοποιήσετε μεταξύ τους δεδομένα (ή ιδιότητες – attributes). Η διαφορά της από έναν πίνακα κατακερματισμού είναι ότι δεν μπορούμε να χρησιμοποιήσουμε μεταβλητές του ίδιου τύπου: κάθε φορά πρέπει να δημιουργούμε ένα νέο πίνακα κατακερματισμού.
Ορίζουμε μια δομή με τη δεσμευμένη λέξη Struct
. Η δομή είναι κι αυτή μια κλάση με τη διαφορά ότι δε διαθέτει μεθόδους.
>> Complex = Struct::new( :x, :y)
>> z = Complex.new(3,2)
>> p "#{z.x}+i#{z.y}"#
Αργά ή γρήγορα θα αναγκαστείτε να σπάσετε τον κώδικά σας, είτε αυτός είναι σε μορφή μεθόδων είτε σε μορφή κλάσεων, σε αυτόνομα τμήματα ώστε να είναι ευκολότερα διαχειρίσιμος. Η Ruby διαθέτει για το σκοπό αυτό τα modules (ενότητες ή αρθρώματα) που είναι αντίστοιχα με τα packages στη Java και τα namespaces στη C#. Μια ενότητα στη Ruby ορίζεται παρόμοια όπως και μια κλάση με τη διαφορά ότι δεν μπορείτε να ορίσετε ιδιότητες (attributes) καθώς δεν μπορείτε ούτε να κληρονομήσετε απ’ αυτή ούτε να την αρχικοποιήσετε (δηλ. να παράγετε αντικείμενα της ενότητας):
module CarUtils
NUM_OF_DOORS = 4
def paint(car, color)
...
end
def repair(car)
...
end
class Wheel
...
end
end
>> puts Car::NUM_OF_DOORS # result: 4
Παρατηρήστε ότι μια δομή (struct) διαθέτει μόνο ιδιότητες (attributes) ενώ μια ενότητα (module) διαθέτει μόνο μεθόδους και σταθερές. Η Ruby παρέχει έναν τρόπο να ενσωματώνει μεθόδους μιας ενότητας σε μια κλάση. Επιτρέπει με άλλα λόγια πολλαπλή κληρονομικότητα (multiple inheritance), η οποία δεν υποστηρίζεται απ’ τη Ruby. Έτσι, μπορείτε να δημιουργήσετε μια κλάση, να κληρονομήσετε από μια άλλη κλάση και να ενσωματώσετε όσες ενότητες χρειάζεστε. Π.χ.
class Car
include CarUtils
...
end
>> puts Car::NUM_OF_DOORS # result: 4
Συμπερασματικά
Μετά από αυτή τη σύντομη περιγραφή της γλώσσας, είδατε τα πλεονεκτήματά της. Η γλώσσα υποστηρίζει ακόμα εξαιρέσεις (exceptions), νήματα (threads) και διεργασίες (processes), διαχείριση αρχείων και όλα όσα χρειάζεστε από μια σύγχρονη γλώσσα προγραμματισμού.
Αν ακόμα δεν έχετε πειστεί για τις δυνατότητες της γλώσσας, δείτε το παρακάτω πρόγραμμα το οποίο εξομοιώνει την εντολή cat
του Unix:
# Imitate the Unix cat utility
# ============================
# Loop across the arguments
ARGV.each { |arg|
# Open the file for reading and write its contents to screen
IO.foreach(arg) {|line| puts line}
}
Συγκρίνετέ το με το πόσες γραμμές κώδικα χρειάζονται για να γραφεί σε Java ή C! Συμπερασματικά, η Ruby συλλέγει τα καλύτερα στοιχεία από πολλές διαδεδομένες γλώσσες προγραμματισμού, προσφέροντας ένα δυναμικό συντακτικό, που σας επιτρέπει να μάθετε γρήγορα προγραμματισμό χωρίς τις παραξενιές άλλων γλωσσών προγραμματισμού.
Αναφορές
- Carlson L., Richardson L., (2006), Ruby Cookbook, O’ Reilly.
- CollingBourne B., (2008), The Little Book of Ruby, 2nd Edition.
- Flanagan D., Matsumoto Y. (2008), The Ruby Programming Language, O’ Reilly.
- Fitzgerald M. (2007), Learning Ruby, O’ Reilly.
- Fitzgerald M. (2007), Ruby Pocket Reference, O’ Reilly.
- Fulton H., (2006), The Ruby Way, Addison Wesley.
- Griffiths D. (2009), Head First Rails, O’ Reilly.
- McAnally J. (2006), Humble Little Ruby Book.
- Ruby & Ruby on Rails, Linux Magazine / France, No 33, Novembre / Decembre 2007.
<- | -> |