Ruby è un linguaggio object-oriented originato in Giappone, e qui molto diffuso, ad opera di Yukihiro Matsumoto nel 1993. Il nome Ruby si rifà a Perl, in italiano rispettivamente rubino e perla.
Ruby è un linguaggio ad oggetti, i tipi di dato sono classi e le istanziazioni sono oggetti, dotati quindi di metodi ed attributi, ad esempio i numeri appartengono alla classe Fixnum, Bignum, Complex o Float; le stringhe alla classe string. Si possono creare nuove classi da una classe esistente, ma non esiste ereditarietà multipla; tuttavia è possibile includere in una classe dei moduli, vale a dire raggruppamenti di metodi, costanti e classi. Nella Figura 2‑3 le classi di alcuni tipi di dato con la gerarchia di appartenenza.
Il grafico di Figura 2‑3 è stato generato da Tcl/Tk (v. par. 2.53 ) richiamato da Ruby tramite[1]:
def
push_itm (lst,input)
# genera array con key nodo e livello,
progressivo, padre
subLevel = 0
if Albero.has_key?(input[0]) then
level = Albero[input[0]][0] + 1
else
level = 1
Albero[input[0]] = [0,1,""] #
presumo radice
end
1.step(input.length-1,1) do |j|
subLevel = subLevel + 1
Albero[input[j]] =[level
,subLevel,input[0]] # presumo radice
end
end
def estrai(nodo) # estrae ricorsivamente i figli
Fl.printf "%2d %2d
%s\n",Albero[nodo][0], Albero[nodo][1], nodo
print "#{nodo} ";p Albero[nodo]
Albero.each_key {|key|
if nodo == Albero[key][2] then
estrai(key)
end
}
end
Albero
= {} # array hash dei nodi
Lista
= ['Object'] # lista nodi in ordine
di arrivo
push_itm(Albero,['Object','String','Numeric'])
push_itm(Albero,['Numeric','Integer','Float','Complex'])
push_itm(Albero,['Integer','Fixnum','Bignum'])
Fl
= File.new("rubyobj.txt",
"w")
estrai("Object")
Fl.close
callTCL
= %Q/wish83.exe tree.tcl rubyobj.txt Gerarchia_di_oggetti_Ruby orizzontale/
exec
callTCL
Il grafico si sarebbe potuto generare da Ruby stesso mediante l'estensione nativa per Tcl/Tk (sono supportati anche altri strumenti grafici quali GTK, OpenGL, ecc…).
Ruby ha una serie impressionante di metodi, funzioni su stringhe, su matrici, spesso ridondanti: un ciclo da 1 a 10 ( o viceversa) può essere scritto come:
9.downto(0) {|i| print i}
9.step(0, -1) {|i| print i}
10.times do |i| print i end
0.upto(9) {|i| print i}
for i in 0..9 do print i end
Dall'esempio si nota l'incongruenza della variabile di ciclo i indicata fra ||,
mentre è libera come argomento di for; un'altra incongruenza
è l'utilizzo di # come commento e come valutatore di espressioni
(peraltro molto utile). Anche in Ruby esistono le scorciatoie sintattiche
quali z
*= b invece di z = z * b o l'assegnazione multipla, dalla
cui complicazione, emerge a,b = b,a con l'effetto di scambiare i
valori di a e b. Sempre a proposito di assegnazione a
= b non genera due oggetti, ma un riferimento; per copiare un oggetto
occorre usare i metodi clone o dup: b
= a.dup.
Una scorciatoia, però piuttosto utile, è il delimitatore %w
che permette di assegnare, in modo diretto, dei valori ad un array. Un'altra
delle ridondanze
del linguaggio è: unless condizione ... che corrisponde
ad if
not condizione ....
Le variabili che iniziano con $ sono variabili globali; i nomi di costanti iniziano con una
lettera maiuscola, tuttavia, e questa è una incongruenza del linguaggio, le
costanti possono essere variate, ed essendo globali, si comportano di fatto
come variabili globali.
Come alcuni altri linguaggi (AWK, in parte PL/SQL), permette di indicare esplicitamente le istruzioni iniziali e finali di un programma tramite i blocchi BEGIN {...} ed END {...}.
Fra i pregi di Ruby c'è la possibilità di scrivere espressioni condizionali (frammento 1) o utilizzare if come funzione che fornisce un valore (frammento 2):
puts "Pericolo!!!" if radiazione > 3000 # frammento 1
a = if rand > 0.5 then "pari" else "dispari" end # frammento 2
Ruby ha un ottimo trattamento del
parallelismo. L'esempio che segue è la simulazione di una divisione fra interi
su una macchina con la sola istruzione di sottrazione (v. par.
Macroassemblatori). Il programma
sorgente è stato generato con il macroprocessore M4 (v. par.Sugli Algoritmi
utilizzati). Nel programma sono eseguiti in parallelo le funzioni interprete, indata (per simulare l'input) e outdata (per simulare l'output).
#
RUBYWIN Platform: i586-mswin32 Version: 1.6.6 - 2001-12-26
def
outdata # invia i dati in output
while
$mem["Output"] == 0 do end # attende che il buffer diventi pieno
print "Output: "; p $mem["Output"]
$mem["Output"] = 0 # pulisce il buffer per nuovi dati
end
def
indata(input) # riceve i dati in input
while $mem["Input"] != 0 do end #
attende che il buffer sia svuotato
$mem["Input"] = input
print "Input: "; p
$mem["Input"]
end
def
interprete
while $mem["PC"] != 3 do
pc = $mem["PC"]
$mem[$mem[pc]] = $mem[$mem[pc]] -
$mem[$mem[pc+1]]
pc2 = $mem["PC"] # se per caso si è modificato PC
if $mem[$mem[pc]] < 0 then
$mem["PC"] = pc2 -
$mem[pc+2].to_i
else
$mem["PC"] = pc2 + 3
end
end
end
#
l'immagine della memoria è una matrice associativa
#
gli indirizzi (chiavi della matrice) di memoria sono:
#
il nome del campo o un numero progressivo a partire dal
#
valore del Program Counter (PC)
$mem
= {}
address
= 0
IO.foreach("unaistr.mcr")
{|line| # lettura sorgente
if line.tr(' ','').length != 1 then # linee non vuote
istr = line.scan(/\S+/) # separo item
if istr[0][0] == ':'[0] then # nome di campo
$mem[istr[0][1..9]] = istr[1].to_i
address = istr[1].to_i if istr[0] ==
':PC'
elsif istr[0] != '*' then # commento
istr.each do |i| # istruzioni
$mem[address] = i
address = address + 1
end
# do |i|
end
# if istr ...
end
# if line
}
thr1
= Thread.new {indata(222);indata(44)}
thr2
= Thread.new {outdata}
thr3
= Thread.new {interprete}
#
.join metodo che attende la fine thread; .join su tutti i threads li
sincronizza
thr1.join;
thr2.join; thr3.join;
Il risultato:
Input:
222
Input:
44
Output:
5
Ruby è un linguaggio introspettivo, ha la capacità di indicare gli oggetti che contiene ed i metodi che questi sopportano.
Nell'esempio che segue il programma determina se una formula di logica preposizionale (tesi), è derivabile da un insieme di formule date (ipotesi). Si presuppone che tesi ed ipotesi siano espresse in forma normale disgiuntiva (v. par. 3.2 ).
# RUBYWIN Platform: i586-mswin32 Version: 1.6.6 - 2001-12-26
# - è not + è or . è and
require "reductio_sub"
def proof(hyth,thesis)
print "hypothesis: "; p hyth
print "thesis: "; p thesis
# separa i componenti in and
arr_hyth =hyth.split('.')
arr_thesis = thesis.split('.')
# separa i componenti in or
arr_hyth.each_index {|itm| arr_hyth[itm] = arr_hyth[itm].split('+')}
arr_thesis.each_index {|itm| arr_thesis[itm] = arr_thesis[itm].split('+')}
# inizio del metodo reductio
arr_thesis.each do |itm| # per ogni subformula della tesi
arr_hyth_val = instantiate(arr_hyth,not_true(itm)) # assegno i valori
$result = verify(arr_hyth_val)
#print " ---> result #{$result} di ";p itm
break if $result == "F"
end
return $result
end
p proof("-a+b.-c+b.a+c","b") # derivabile
p proof("-a+b.-c+b.a+c","-b") # non derivabile
p proof("-a+b.-c+b.a+c","b+a") # derivabile
p proof("-a+b.-c+b.a+c","b.a") # non derivabile
p proof("-a+b.-c+b.a+c","x+y") # non derivabile
p proof("-a+b.-c+b.-a.a","b") # inconsistente -a.a (tutto è derivabile)
p proof("-a+b.-b+c.-c+d.a.e.-d+g.-e+c","g") # derivabile
# RUBYWIN Platform: i586-mswin32 Version: 1.6.6 - 2001-12-26
def not_true(arr_of_var)
# genera una lista variabile-valore, che rendono
# falso or di arr_of_var
list = []
(arr_of_var).each do |itm|
list.push [itm, "F"]
list.push ["-"+itm, "T"]
list[-1,1][0][0] = itm[-1].chr if itm[0] == "-"[0]
end
return list
end
def instantiate(formula,values)
# sostituisce in formula i valori assegnati alle variabili
form = []
formula.each do |frm|
subform = []
frm.each do |var|
variab = var
values.each_index do |indx|
variab = values[indx][1] if var == values[indx][0]
end
subform << variab
end
form << subform
end
return form
end
def set_value(frm,n)
# imposta i valori delle variabili in funzione del bit binario di n
list = []
frm.length.times do |i|
j = n[i] # bit in position i
list.push [frm[i], "FT"[j,1]]
list.push ["-"+frm[i], "TF"[j,1]]
list[-1,1][0][0] = frm[i,1][0][1].chr if frm[i][0] == "-"[0]
end
return list
end
def verify(hyth_frm)
$result = "F"
hyth_frm.each_index do |frm|
$result = "T" if hyth_frm[frm].uniq == ["F"]
# return T se tutte le variabili sono F(alse)
unless hyth_frm[frm].include? "T"
vrb = hyth_frm[frm].dup.delete_if {|x| x == "T" or x == "F"}
1.upto(2**vrb.length-1) do |n|
arr_left = instantiate(hyth_frm[frm..hyth_frm.size],set_value(vrb,n))
$result = verify(arr_left)
break if $result == "F"
end # do |n|
return $result
end # unless hyth_frm[frm].include? "T"
end # do |frm|
return $result
end # verify
Il risultato è quanto segue, si noti la sesta derivazione, che il sistema riconosce verificata, ciò è dovuto alla inconsistenza delle ipotesi in quanto contengono la formula sempre falsa non a e a (-a.a).
hypothesis:
"-a+b.-c+b.a+c"
thesis: "b"
"T"
hypothesis:
"-a+b.-c+b.a+c"
thesis: "-b"
"F"
hypothesis:
"-a+b.-c+b.a+c"
thesis: "b+a"
"T"
hypothesis:
"-a+b.-c+b.a+c"
thesis: "b.a"
"F"
hypothesis:
"-a+b.-c+b.a+c"
thesis: "x+y"
"F"
hypothesis:
"-a+b.-c+b.-a.a"
thesis: "b"
"T"
hypothesis:
"-a+b.-b+c.-c+d.a.e.-d+g.-e+c"
thesis: "g"
"T"
[1] Il grafico si sarebbe
potuto generare da Ruby stesso mediante l'estensione nativa per Tcl/Tk (sono
supportati anche altri strumenti grafici quali
GTK, OpenGL, ecc…).