Da C/C++ a Ruby

È difficile descrivere in una lista come del codice in Ruby differisce da C o C++, dal momento che esistono delle notevoli differenze. Una ragione è che il runtime di Ruby fa molte cose per lo sviluppatore. Ruby sembra il più distante possibile dal principio di C “nessun meccanismo nascosto”—lo scopo di Ruby è rendere il lavoro umano facile a spese del runtime, che svolge gran parte del lavoro. A meno a che (o fino a quando) non decidi di analizzare il tuo codice per ottimizzarlo, non devi preoccuparti di “far felice il compilatore” quando usi Ruby.

Detto questo, il codice scritto in Ruby viene eseguito molto più lentamente del codice equivalente in C o C++ . Allo stesso tempo, ti meraviglierai di quanto poco tempo ti ci vorrà per avere un programma in Ruby pronto e funzionante e anche di quante righe di codice in meno ti ci vorranno per scriverlo. Ruby è molto, molto più semplice di C++: ti vizierà in men che non si dica!

Ruby è tipizzato dinamicamente, invece che staticamente—il runtime fa quanto più possibile quando il programma è in esecuzione. Per esempio, non devi sapere a quanti moduli il tuo programma in Ruby si “linkerà” (ovvero: caricherà e userà), o di quanti metodi chiamerà prima in anticipo.

Inoltre, Ruby e C sono in stretta relazione: Ruby supporta i “moduli di estensione”, che possono essere chiamati da programmi scritti in Ruby e che, dall’esterno, si comportano proprio come altri moduli di Ruby, ma sono scritti in C. In questo modo, puoi rendere più performanti parti critiche della tua applicazione scrivendole in puro C.

E non dimentichiamo che, ovviamante, anche Ruby stesso è scritto in C.

Somiglianze con C

Come in C, in Ruby…

  • Puoi programmare in modo procedurale, se vuoi (ma in realtà Ruby lavorerà sempre in maniera orientata agli oggetti, a un livello più basso)
  • Molti degli operatori sono gli stessi, inclusi le assegnazioni compound e gli operatori per bit, però Ruby non ha ++ o --
  • Esistono __FILE__ e __LINE__.
  • Esistono le costanti, ma non si usa la parola chiave const. Le costanti create attraverso la convenzione che i nomi devono incominciare per una lettera maiuscola.
  • Le stringhe sono racchiuse in doppi apici.
  • Le stringhe sono mutabili.
  • Puoi leggere la maggior parte dei documenti da riga di comando, attraverso il comando ri
  • Esiste una sorta di debugger da riga di comando.

Somiglianze con C++

Come in C++, in Ruby…

  • Esistono più o meno gli stessi operatori (anche ::). L’operatore << è usato spesso per aggiungere elementi ad una lista. Ruby tuttavia non usa mai l’operatore ->, è sempre solo ..
  • public, private e protected si comportano in modo simile
  • La sintassi per l’ereditarietà è sempre costituita da un solo carattere, ma è < invece di :.
  • È possibile raggruppare il tuo codice in moduli, usati in modo simile ai namespace.
  • Le eccezioni lavorano nello stesso modo, ma i nomi delle parole chiave sono diversi.

Differenze da C

A differenza di C, in Ruby…

  • Gli oggetti sono fortemente tipizzati (e i nomi di variabili in sè stessi non hanno nessun tipo)
  • Non ci sono nè macro nè preprocessore. Non ci sono operazioni di cast, puntatori, typedef, sizeof o enum,
  • Non ci sono file header. Bisogna definire le funzioni (normalmente chiamate “metodi”) e classi nei file sorgenti
  • Non esiste il #define, ma vengono usate delle costanti
  • Da Ruby 1.8 in poi, il codice è interpretato a runtime invece di essere compilato in qualsiasi tipo di codice macchina o simili.
  • Tutte le variabili sono allocate nella heap. Inoltre, non è necessario distruggerle a mano—il garbage collector lo fa automaticamente.
  • Gli argomenti dei metodi sono passati per valore, dove il valore è sempre una reference.
  • Bisogna scrivere require 'xxx' invece di #include <xxx> o #include "xxx".
  • Non si può ricorrere all’assembly
  • Non si usano punti e virgola alla fine di ogni riga
  • Si possono omettere parentesi in espressioni condizionali come if e while
  • Le parentesi sono opzionali nelle chiamate ai metodi
  • Normalmente non si utilizzano le parentesi graffe, i costrutti su più righe si concludono con la keyword end
  • La keyword do è per i cosiddetti “blocchi”; non esistono costrutti con il “do” come in C.
  • Il termine “blocco” si riferisce ad un blocco di codice che viene associato ad una chiamata ad un metodo in modo che il metodo possa chiamare il codice nel blocco durante l’esecuzione.
  • Non ci sono dichiarazioni di variabili: è sufficiente solo assegnare nuovi nomi al momento, ove necessario.
  • Solo false e nil risultano essere dei valori falsi, ogni altra cosa ritorna “true” (inclusi 0, 0.0 e "0")
  • Non esistono char, ma solamente delle stringhe di un solo carattere.
  • Le stringhe non terminano con un byte nullo.
  • Gli elementi di un array sono racchiusi in parentesi quadre, non graffe.
  • Gli array diventano più grandi mano a mano che vengono aggiunti dei nuovi elementi al loro interno
  • Se due array vengono sommati, viene ritornato un nuovo array più grande (sempre allocato nella heap). Non bisogna ricorrere all’aritmetica dei puntatori.
  • Generalmente, ogni cosa è un’espressione (ovvero cose come costrutti while ritornano sempre un rvalue)

Differenze da C++

A differenza di C++, in Ruby…

  • Non ci sono riferimenti expliciti. Ovvero ogni variabile è di fatto un nome di un’oggetto deferenziato automaticamente.
  • Gli oggetti sono tipizzati fortemente ma dinamicamente. Il runtime capisce automaticamente a runtime che se un metodo funziona oppure no.
  • Il “costruttore” è chiamado initialize invece di avere lo stesso nome della classe.
  • Tutti i metodi sono sempre virtuali.
  • I nomi delle variabili di classe (statiche) incominciano sempre con @, es. @totale_oggetti.
  • Non si accede alle variabili membro, l’accesso ai membri pubblici (chiamati attributi in Ruby) è effettuato tramite metodi.
  • Si usa self e non this
  • Alcuni metodi finiscono con ’?’ o ’!’, che è di fatto parte del nome del metodo stesso.
  • Non esiste l’ereditarietà multipla, anche se Ruby ha dei “miscugli” (ovvero si possono “ereditare” tutti i metodi di istanza di un modulo)
  • Esistono delle convenzioni particolari sulle maiuscole e minuscole, che vanno rispettate (es. i nomi delle classi devono iniziare sempre con una lettera maiuscola, le variabili con una lettera minuscola).
  • Le parentesi per le chiamate ai metodi sono normalente opzionali.
  • Non c’è bisocno dei template di C++, dal momento che è possibile assegnare qualsiasi tipo di oggetto ad una particolare variabile, e i tipi vengono determinati a runtime. Non c’è nemmeno nessun tipo di casting.
  • L’iterazione avviene in modo leggermente diverso. Ruby non utilizza oggetti separati come iteratori (come vector<T>::const_iterator iter) ma gli oggetti possono “mescolarsi” con il modulo Enumerator ed effettuare una chiamata ad un metodo, come oggetto.each.
  • Esistono solo due tipi di contenitori: Array e Hash.
  • Non ci sono conversioni di tipo, ma non sono necessarie in Ruby.
  • Il multithread è disponibile di default, ma da Ruby 1.8 si usano dei “green thread” (implementati solo all’interno dell’interpreter) e non dei thread nativi.
  • Una libreria standard di unit test è disponibile di default.