|
Una tuple (che di seguito chiamerò ennupla) è una collezione immutabile, indicizzata e allocata nello stack di elementi di tipo diverso. Molto simile ad un array ma, come detto immutabile anche nella documentazione ufficiale viene definito come array immutabile. Formalmente tra l'altro sta a metà tra un hash e, appunto, un array: {elemento-1, elemento-2, ... elemento-n} ovvero una sequenza di elementi separati da una virgola all'interno di una coppia di parentesi graffe, ad esempio: t01 = {1, 'a', 0.1, "aaa"} Questo è il modo più semplice per definire una ennupla Oppure si ricorre al solito new passando gli argomenti che vogliamo t01 = Tuple.new(1, 'a', "aaa") Se abbiamo un array a disposizione possiamo usare from, anche se come sistema non mi pare molto comodo. Bisogna mappare, con un cast compatibile ovviamente, tutti gli elementi dell'array nella ennupla: ar1 = [1,2,3,4] t01 = Tuple(Int32, Int32, Int32, Int32).from(ar1) L'operatore per estrarre il singolo elemento è il solito [] tenendo presente che anche in questo costrutto vale la solita indicizzazione sequenziale per interi partenti da zero e anche qui è possibile avere una indicizzazione con numeri negativi. Come ormai dovrebbe essere consueto, si può usare l'operatore []? per controllare i casi di indici non validi. Da notare che dal momento che il compilatore in questo caso conosce perfettamente l'allocazione di ogni singolo elemento, selezionare un indice non valido con [] è un evento che viene intercettato a compile time. L'operatore [] ci permette anche di estrarre dei range: t01 = {1, 2, "aa", 0.1} x01 = t01[1..3] puts typeof(x01) Tuttavia non potete usarlo per sostituire un elemento della ennupla con un altro stante l'immutabilità di questo costrutto. Interessante è anche il metodo at(indice) che ci permette anche un messaggio personalizzato in caso di indice : t01 = {1, "aa", 'a'} puts t01.at(1) {puts "not found"} puts t01.at(71) {puts "not found"} output:
Può essere comodo, invece usare questo metodo per attribuire un valore di default ad una variabile in caso di uso di indici scorretti: t01 = {1, "aa", 'a', 2} x = t01.at(1) y = t01.at(5) {10} puts x puts y output:
Per controllare la dimensione, ovvero il numero di elementi di una ennupla si usa, come al solito, size, banalmente ad esempio puts t01.size in uno dei tanti esempi precedenti. Per quanto riguarda il resto dei metodi possibili per una ennupla, non ci sono grandi particolarità da segnalare da quanto già visto con le altre sequenze, li vediamo in un esempio e li commentiamo:
Se volete iterare tra gli elementi di una ennupla c'è disponibile il solito each e il omologo reverse_each che lavora allo stesso modo all'incontrario:
Ovviamente potete usare anche una delle semplici iterazioni che già conosciamo:
Una variante interessante delle ennuple sono le ennuple con nome (Named tuple, in inglese). Queste si distinguono dalle ennuple "normali" per il fatto che ad ogni elemento possiamo associare un nome con cui recuperarlo, questo certamente ci ricorda il meccanismo chiave valore di un hash. Concettualmente si tratta di costrutti molto simili la differenza principale è che un NamedTuple è immutabile ed è noto, nella sua conformazione definitiva, già a compile time. In aggiunta, una NamedTuple è immutabile mentre una hash può essere modificata. nt01 = {anno: 1990, mese: "agosto"} puts nt01[:anno] # 1990 puts nt01[:mese] # agosto Davvero molto simile ad un hash. Si possono ricavare chiavi e valori: puts nt01.keys puts nt01.values e si può attraversare nello stesso modo visto per gli hash: nt01 = {anno: 1990, mese: "agosto"} nt01.each {|chiave, valore| puts "la chiave #{chiave} ha valore #{valore}" } Le ennuple le ritroveremo parlando delle funzioni in quanto sono un partner privilegiato di queste per questioni legate alla ottimale gestione della memoria ma soprattutto in quanto struttura perfetta per restituire più parametri di ritorno dalle funzioni stesse come vedremo. |