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