Lezione del 14/3/2014¶
Riassuntino `hash`
File 4.1_intro_hash.rb
1#!/usr/bin/env ruby
2#-------------------------------------------------------------------------#
3# Esercitazioni in Laboratorio per il Corso di #
4# Fondamenti di Informatica e Calcolo Numerico, AA 2013/2014 #
5# #
6# Autori: Enrico Bertolazzi e Carlos Maximiliano Giorgio Bort #
7# Dipartimento di Ingeneria Industriale, Universita` di Trento #
8# Sito web: http://www.ing.unitn.it/~bertolaz/ #
9# #
10# Contatti: enrico.bertolazzi@unitn.it, cm.giorgiobort@unitn.it #
11# #
12# Copyright (c) 2014 E.Bertolazzi e C.M. Giorgio Bort #
13#-------------------------------------------------------------------------#
14
15# una hash e` un Array indicizzato, nel quale per accedere ad ogni elemento
16# non uso il numero dell'indice corrispondente, bensì una chiave.
17# Quest'ultima tipicamente e` un Symbol.
18
19### I SIMBOLI
20# i Symbol rappresentano nomi all'interno dell'interprete di Ruby. sono così definiti:
21s = :a
22
23# posso ottenere simboli a partire da stringhe, convertendo una stringa in un simbolo
24str = "simbolo"
25sym1 = str.to_sym
26puts sym1
27#oppure, in modo equivalente
28sym2 = str.intern
29puts sym2
30# questo secondo metodo rende più chiaro che si sta cercando la
31# rappresentazione interna all'interprete di 'str' se la mia stringa
32# comprende anche spazi bianchi, il simbolo che ottengo diventa:
33str = 'il mio simbolo'
34sym = str.to_sym
35puts sym
36# però vedo che il mio simbolo non e` elegante, in quanto a causa
37# degli spazi bianchi nella stringa, Ruby mantiene le virgolette
38# Se ho una stringa con spazi bianchi e la voglio convertire in
39# un simbolo la cui rappresentazione sia più elegante e comprensibile
40# posso fare in modo di mettere degli 'underscore' al posto degli spazi bianchi
41sym = str.gsub(/\s+/, "_").downcase.to_sym
42puts sym
43# il comando '.gsub(/\s+/, "_")' sostituisce gli spazi bianchi nella
44# stringa con degli underscore "_". Questa operazione e` effettuata
45# ricorrendo alle espressioni regolari (le vedremo più avanti).
46# Per il momento vi basti sapere che nell'esempio qui mostrato
47# l'espressione regolare e` '/\s+/' e mi permette di trovare tutti
48# gli spazi bianchi nella stringa.
49
50# Posso anche convertire i numeri in simboli.
51# Tuttavia, non esiste un metodo .to_sym per i numeri,
52# infatti Ruby mi da un errore se scrivo:
53a = 3
54a.to_s.to_sym # oppure a.intern
55puts a
56# quello che devo fare e` prima rappresentare 'a' come una
57# stringa ('to_s') e poi come un simbolo ('to_sym'):
58a = 3
59puts a.to_s.to_sym
60# questo e` necessario perche` i simboli sono il modo con cui
61# l'interprete di Ruby rappresenta i NOMI (i.e. le stringhe).
62# Per i numeri la logica interna di rappresentazione e` diversa.
63# Per questo motivo in generale non ha mai senso convertire numeri a simboli.
64
65
66### LE HASH
67# Definisco una hash in Ruby 1.8.7
68cane = { :nome => "Rex", :anni => 7} # noi useremo sempre questa notazione
69# definisco una hash in Ruby > 2.0.0
70gatto = { nome: "Cesar", anni: 3}
71
72# posso aggiungere dinamicamente chiavi alla mia hash
73cane[:razza] = :dalmata
74# in una hash posso mettere anche degli Array
75cane[:colore] = ["nero", "bianco"]
76puts cane
77
78# Posso anche creare una hash vuota e aggiungere dinamicamente elementi
79io = {}
80io[:nome] = "Maximiliano"
81io[:sesso] = :maschio
82
83# La hash può avere delle chiavi che puntano ad altre hash
84io[:animali] = {:c => cane, :g => gatto}
85puts io
86
87# in questo caso, per sapere quanti anni ha il mio cane dovrò digitare
88io[:animali][:c][:anni]
89
90
91
92
93
94# Fino ad ora abbiamo visto che le chiavi sono dei Simboli,
95# tuttavia posso definire come chiavi qualsiasi cosa:
96h = {
97 :sym => "simbolo",
98 3 => 33 ,
99 "stringa" => true,
100 [1,2,3] => ["uno", "due", "tre", "quattro"],
101 {:a => 'a'} => "hash nella hash"
102}
103# ma la maggior parte delle volte usare chiavi che non sono
104# simboli e` molto scomodo e può portare ad errori:
105puts h[:sym]
106puts h[3]
107puts h[2] # questo mi da nil perche` non ho nessuna chiave pari a '2'
108puts h['stringa']
109puts h[ [1,2,3] ]
110puts h[ {:a => 'a'} ]
111
112
113
114### ITERARE IN UNA HASH
115# Per ottenere il numero degli elementi nella hash, posso usare '.size' o '.length'
116puts cane.size
117
118# se ho hash annidate, il metodo '.size' (o '.length')
119# mi restituisce il numero degli elementi nella hash "radice"
120puts io.size
121
122# Dal momento che non posso accedere agli elementi della hash
123# con degli indici interi, non posso iterare nella hash con un ciclo FOR.
124# Per iterare nella hash sono costretto ad usare il metodo '.each'
125cane.each do |chiave, valore|
126 puts "#{chiave} corrisponde a #{valore}"
127end
128# in modo compatto:
129cane.each{ |chiave, valore| puts "#{chiave} corrisponde a #{valore}" }
130
131# posso iterare soltanto nelle chiavi della hash:
132cane.each_key do |k|
133 puts "#{k} e` una chiave di 'cane'"
134end
135
136# oppure posso iterare soltanto nei valori della hash
137cane.each_value{ |v| puts "#{v} e` un valore della hash 'cane'" }
138
139# tutti gli altri metodi delle hash sono visibili con il solito comando:
140puts cane.methods.sort
Esercizi sulle `Hash`
File 4.2_esercizi_hash.rb
1#-------------------------------------------------------------------------#
2# Esercitazioni in Laboratorio per il Corso di #
3# Fondamenti di Informatica e Calcolo Numerico, AA 2013/2014 #
4# #
5# Autori: Enrico Bertolazzi e Carlos Maximiliano Giorgio Bort #
6# Dipartimento di Ingeneria Industriale, Universita` di Trento #
7# Sito web: http://www.ing.unitn.it/~bertolaz/ #
8# #
9# Contatti: enrico.bertolazzi@unitn.it, cm.giorgiobort@unitn.it #
10# #
11# Copyright (c) 2014 E.Bertolazzi e C.M. Giorgio Bort #
12#-------------------------------------------------------------------------#
13
14# A) Usare le hash per costruire un elenco telefonico
15elenco = [
16 {:nome => "Carlo",
17 :cognome => "Rossi",
18 :numero => 1231231230,
19 :mail => 'carlo.rossi@mail.it'},
20 {:nome => "Stefan",
21 :cognome => "Benz",
22 :numero => 22211223,
23 :mail => 'ste123@mail.de'},
24 {:nome => "Maria",
25 :cognome => "Callas",
26 :numero => 444001231,
27 :mail => 'callas@mail.com'}
28]
29
30# A.1) Per ogni persona nell'elenco, farsi stampare
31# sul terminale la descrizione, formattata come:
32# "NOME COGNOME: tel. NUMERO , mail MAIL"
33
34puts "Esempio 1"
35
36elenco.each do |elem|
37 # elem contiene la hash i-esime del array elenco
38 # dove i va da 0 a elenco.length-1
39 # estraggo valori della hash
40 n = elem[:nome]
41 c = elem[:cognome]
42 tel = elem[:numero]
43 m = elem[:mail]
44 # stampo come richiesto
45 puts "#{n} #{c}: tel. #{tel}, mail #{m}"
46end
47
48# A.2) Farsi stampare sul terminale la descrizione di ogni
49# contatto nell'elenco telefonico (come nell'es. A.1),
50# ma ordinando i cognomi in ordine alfabetico
51
52#
53# MODO COMPLICATO
54#
55# step 1, extraggo i cognomi e li riordino.
56# Il metodo collect cotruisce un Array iterando sugli elementi del vettore
57cognomi = elenco.collect { |elem| elem[:cognome] }
58cognomi.sort! # il metodo sort riordina l'array
59
60# step 2, costruisco una mappa che dal cognome trova
61# la posizione nel vettore elenco
62map_to_position = {}
63elenco.each_with_index { |elem,i| map_to_position[elem[:cognome]] = i }
64# each_with_index oltre che a prendere l'elemento
65# aggiunge un secondo argomento con la sua posizione
66
67# step 3, stampa lista ordinata
68puts "Esempio 2"
69cognomi.each do |c| # c contiene il cognome
70 pos = map_to_position[c] # dal cognome trovo la posizione
71 elem = elenco[pos] # prendo la Hash dall'elenco
72 # stampo come richiesto
73 puts "#{elem[:nome]} #{elem[:cognome]}: tel. #{tel = elem[:numero]}, mail #{elem[:mail]}"
74end
75
76#
77# MODO SEMPLICE
78#
79# riordino il vettore elenco, non posso usare elenco.sort!
80# perche sort! non sa come confrontare 2 hash
81# devo quindi indicare all'interprete come
82# confrontare sue hash
83elenco.sort! { |a,b| a[:cognome] <=> b[:cognome] }
84# il blocco {} restituisce
85# -1 se a < b (secondo le nostre regole)
86# 0 se a == b (secondo le nostre regole)
87# +1 se a > b (secondo le nostre regole)
88# l'operatore <=> applicato a 2 stringhe s1 e s2 restituisce
89# -1 se s1 < s2, 0 se s1 == s2 ed +1 se s1 > s2
90
91puts "Esempio 3"
92elenco.each do |elem|
93 # elem contiene la hash i-esime del array elenco
94 # dove i va da 0 a elenco.length-1
95 # stampo come richiesto
96 puts "#{elem[:nome]} #{elem[:cognome]}: tel. #{tel = elem[:numero]}, mail #{elem[:mail]}"
97end