(Sözlük, dictionary, birleşik array, map)

14
hash
(Sözlük, dictionary, birleşik array, map)
14.1
Hash Sınıfı
Array ambarı, içindekileri tamsayılarla (index) damgalayıp sıralayan harika
bir yapıdır. Ama bazı durumlarda indis ya yoktur ya da kullanılması kolay
olmaz. Örneğin, bir sınıfta her öğrenciyi numarası ile belirlemek mümkündür, ama pratik değildir. Onun yerine soyadlarını adlarına eşleyen bir liste
daha kullanışlı olur.
hash, { } parantezi içine konulan anahtar => değer ikililerinden oluşan yapısal bir tiptir; bir sınıftır. Bazı dillerde sözlük, associative memories,
associative arrays, map ya da hash tables adlarıyla bilinir. Adından da anlaşılacağı üzere, hash yapısı bir sözlük yapısı gibidir. Sözlükteki terimler
anahtar, açıklamaları ise değer rolünü oynar. Ancak sözlüklerde terimler
(anahtarlar) alfabetik (lexicographic) sıralıdır. hash yapısında anahtarlar
sıralı olmayabilir. Anahtarlar ve değerler Ruby nesneleridir. Sözlüklerde olduğu gibi, anahtarlar tekrarlamayan tiplerden olmalıdır. Değerler her tipten
olabilir. Tekrarlayabilirler. Başka bir deyişle, farklı anahtarlara aynı değer
eşleşebilir (bkz. [10]).
Bu kitapta, hash terimi yerine, çoğunlukla sözlük terimini kullanacağız. Ancak, bir dilin anahtar sözcüklerini değiştirmenin mümkün ya da
uygun olmadığı deyimler vardır. O durumlarda, Ruby’nin kullandığı hash
terimini kullanacağız.
174
BÖLÜM 14. HASH
Arkadaşlarınızın, dostlarınızın adları karşısında telefon numaralarını
yazdığınız basit bir liste, Ruby’de bir sözlük yapısıdır.
Ruby’de hash ambarına konulan öğe çiftleri { } parantezi içine konulur. Anahtarın değere eşlendiği => simgesiyle gösterilir: anahtar=>değer.
Örnek 14.1.
t e l e f o n = { " Aren "=> ’ 0597 123 1234 ’ , " Meltem "=> ’ 01321 654 6789 ’ }
Bu yapının öğeleri anahtar=>değer çiftlerinden oluşur. Öğe sayısı istediğiniz kadar artırılabilir.
Ülke adlarının karşısında telefon kodları yazılı basit bir liste, Ruby’de
bir sözlük yapısıdır.
Örnek 14.2.
u l u s l a r _ t l f = { ’ABD ’ => 1 , ’ Almanya ’ => 49 , ’ Fransa ’ => 33 , ’
T ü r k iye ’=> 90}
Bu ambarda yalnızca dört tane anahtar => değer çifti vardır. Ama listeyi
bütün ülkeleri içerecek kadar uzatabilirsiniz.
İl adları karşısına trafik kodları yazılı basit bir liste, Ruby’de bir sözlük yapısıdır.
Örnek 14.3.
t r a f i k = { ’ B i t l i s ’ => " 13 " , ’ Van ’ => " 65 " , ’ D e n i z l i ’ => " 20 " , ’ Konya
’ => " 42 " , ’ Adana ’ => " 01 " }
Bu ambarda yalnızca beş tane anahtar => değer çifti vardır. Ama listeyi
bütün illeri içerecek kadar uzatabilirsiniz.
Unutmayınız, hash yapısında anahtarlar tekrarlamaz, ama birden çok
anahtara tek bir değer karşılık gelebilir. Örneğin, bir kurumda çalışanların
ofis numaralarını gösteren bir sözlük yapısında, aynı ofisi paylaşanlar için
ofis numaraları aynı olacaktır. Bir kurumda çalışanların ofislerini gösteren
aşağıdaki memur => ofis biçimindeki listede Melis ile Yusuf 23 numaralı
ofiste oturmaktadırlar. Listede, onlar için anahtarlar farklıdır, ama eğerler
aynıdır.
Örnek 14.4.
y e r l e ş i m = { ’ M e l i s ’ => 2 3 , ’ Yusuf ’ => 2 3 , ’ Altan ’ => 25 } ;
Bu sözlük’te ilk iki öğenin anahtarları farklı, ama değerleri aynıdır.
14.2. SÖZLÜK YAPISI ÜZERINDE İŞLEMLER
14.2
175
Sözlük Yapısı Üzerinde İşlemler
Sözlük yapısı bir veri ambarıdır. Buna sözlük ambarı ya da kısaca ambar
diyelim. Bu ambar üzerinde yapılacak başlıca işlemler şunlardır.
• Ambara sözlük öğesi; yani anahtar:değer çifti koyma
• Ambarda olan öğeleri görme
• Ambarda bir anahtarın olup olmadığını anlama
• Ambardaki öğeleri anahtar sırasına koyma
• Ambardan sözlük öğesi çıkarıp kullanma
• Ambardan sözlük öğesi atma (silme)
14.3
Sözlük Yaratma
Bir sözlük yaratmanın farklı yöntemleri vardır. Başlıcalarını örneklerle açıklayalım.
Boş sözlük Yaratma: { } parantezi boş bir sözlük yaratır.
Örnek 14.5.
d = {}
d . length
# => 0
Hash sınıfından Türetme: new operatörü Hash sınıfından boş bir sözlük yaratır.
Örnek 14.6.
d = Hash . new
d . l e n g t h # => 0
anahtar => değer Çiftlerini Yazmak: { } parantezi içine sözlük yapısında yer alacak anahtar:değer çiftleri yazılabilir. Örneğin, bir işyerinde müdür, şef ve sekreterin ofis numaralarını gösteren
Örnek 14.7.
176
BÖLÜM 14. HASH
o f i s = { " müdür " => 310 , " ş e f " => 311 , " s e k r e t e r 1 " => 312 , "
s e k r e t e r 2 " => 312}
listesi bir sözlük yapısıdır. Bu yapıda anahtarlar tektir. Ama sekreter1 ve sekterer2 için ortak bir ofis ayrılmıştır. Bu demektir ki, sözlük
yapısında anahtarlar tek olur, tekrarlamazlar, ama değerler tek olmayabilir.
14.3.1
Array ile Hash Yapılarının Karşılaştırılması
Sözlük yapısı, array yapısı gibidir. Array yapısında her öğenin bir tamsayı
indisi vardır. array’in öğelerine indisleriyle erişilir. Array’in öğeleri indislerine göre sıralıdır. Sözlük yapısında ise öğelerin indisleri yoktur. Onun
yerine anahtarlar vardır. Anahtarlar indis rolünü oynarlar. Sözlük içindeki
her öğe bir anahtar => değer çiftidir. Sözlükte öğe çiftlerine anahtar ile
erişilir. Ama sözlük yapısı anahtarlara göre sıralı olmayabilir.
Sözlük Yaratma
neğin,
Sözlük, Hash sınıfından ::new metodu ile yaratılır. Ör-
puan = Hash . new
deyimi puan adlı bir sözlük ambarı yaratır.
14.4
Hash Ambarına Öğe Koyma
puan [ " Altan " ] = 75
deyimi puan adlı hash ambarına "Altan" => 75 çiftini bir öğe olarak koyar.
Burada "Altan" anahtar, 75 ona karşılık getirilen değerdir. Bir hash ambarına istenildiği kadar öğe konulabilir. Ama anahtar tekrar edemez. Var
olan bir anahtar tekrar kullanılırsa, ona önceden verilen değer silinir, yeni
değeri eskisinin yerine geçer.
Sözlük ambarı yaratılırken içine öğeler koymak mümkündür:
k e n t = { ’ Ankara ’ => 3 1 2 , ’ İ s t a n b u l ’ => 2 1 2 , ’ İ z m i r ’ => 232}
deyimi kent adlı bir sözlük ambarı yaratır ve içine üç kentin adlarını telefon
kodlarına eşleyen üç öğe koyar. Daha sonra bu ambara yeni ekler yapılabilir:
k e n t [ ’ Antalya ’ ] = 242
Anahtardan sonra gelen ok (=>) simgesi, anahtarı değere eşler. O nedenle,
bazı kaynaklarda hash yapısına eşlem (map) denilir.
14.5. SÖZLÜK YAPISININ METOTLARI
177
Hash ambarında olmayan bir öğeye erişilmek istenildiğinde nil öntanımlı değeri gelir. Ama istersek bunu değiştirip, istediğimiz bir nesneyi
öntanımlı yapabiliriz. Örneğin,
h a f t a = Hash . new ( " İ y i g ü n l e r " )
deyimi hafta adlı hash ambarı için "İyi günler" nesnesini öntanımlı yapar.
Öğesi olmayan sözlük ambarına erişim istendiğinde nil yerine, tanımlanan
bu değer gelir. O nedenle, tanımlanan bu değere sözlük ambarının öntanımlı değeri diyeceğiz. Başka öntanımlı değer tanımlanmamışsa, boş sözlük
ambarına erişim istendiğinde nil gelir. nil sözlük ambarının boş olduğu anlamına gelir. Örneğin,
h = Hash . new
h . d e f a u l t # => n i l
olur. Ama, new operatöründe argüman kullanılırsa, aşağıdaki gibi olur:
h = Hash . new ( 0 )
h . d e f a u l t # => 0
Ruby sözlük yapısında anahtar => değer çiftlerini oluşturan anahtarlar ve değerler her tipten olabilirler. Onlar birer nesnedir. Tek koşul,
ambarda anahtar’ın tekrarlamamasıdır. Aynı nesne iki kez anahtar olarak
kullanılamaz. Oysa, değer nesnesi birden çok kez yer alabilir.
14.5
Sözlük Yapısının Metotları
Hash sınıfının çok sayıda metodu vardır. Onların listesini görmek için,
Ruby’nin genel kuralını uygulayabiliriz. Bilindiği gibi, nesne.methods deyimi, sökonusu nesneye uygulanabilen bütün metotları listeler. Hash sınıfına uygulanabilen bütün metotları görmek için
Hash . methods
deyimi yeterlidir. Bu deyim uzun bir liste verir. Burada Hash sınıfının çok
kullanılan bazı metotlarını örneklerle açıklayacağız.
Örnek 14.8.
d = { " Ankara " => 2 1 3 , " i z m i r " => ’ liman ’ , " Elma " => f a l s e }
e = { ’ ab ’=> 1 2 , ’ cd ’ => " k e n t " , ’ e f ’ => t r u e }
deyimleri d ve e adlarında iki sözlük ambarı yaratır ve { } içine, istenen
öğeleri koyar.
Uyarı 14.1. Nesne tabanlı programlama dillerinin çoğunda, her sınıfın
metotları vardır. Bir sınıftan başka bir sınıftaki metot çağrılabilir. Ancak,
178
BÖLÜM 14. HASH
Ruby’de durum farklıdır. Ruby’de metotlar modüllerde toplanır. Kernel
adlı modülde toplanan metotlar, hemen her sınıfa uygulanabilir. Kernel
Module’den üretilmiştir. Kernel’in metotları Ruby nesnelerinin orta malı
gibidir.
Şimdi amradaki öğelerin bileşenlerine nasıl erişileceğini görelim:
14.5.1
Anahtarlara Erişim
keys metodu sözlükteki anahtarlara erişir:
Örnek 14.9.
d . keys
# => [ " Ankara " , " i z m i r " , " Elma " ]
p ( d . k e y s ) # =>["Ankara " , " i z m i r " , " Elma " ]
Burada p() metodu print() metodunun kısa adıdır. Parametresini (argument) yazar.
14.5.2
Değerlere Erişim
values metodu sözlükteki değerlere erişir:
Örnek 14.10.
d . values
# => [ 2 1 3 , " liman " , f a l s e ]
p ( d . v a l u e s ) # => [ 2 1 3 , " liman " , f a l s e ]
14.5.3
Kümesel İşlemler
Sözlük ambarının anahtarları ve değerleri birer array olarak elde edilebildiğine göre, arrayler üzerinde yapılan kümesel arakesit (&), bileşim (+) ve
fark (-) işlemleri uygulanabilir.
Örnek 14.11.
3
d . keys & e . keys
d . keys + e . keys
d . keys − e . keys
Bu kodları etkileşimli (irb) kipinde tek tek yazmak yerine hepsini bir
program haline de getirebiliriz:
Örnek 14.12.
14.5. SÖZLÜK YAPISININ METOTLARI
179
# e n c o d i n g u t f −8
2
d = { " ankara " => ’ k e n t ’ , " i z m i r " => ’ liman ’ , " k a r s " => ’ hudut ’ }
e = { ’ ab ’=> 1 2 , " cd " => ’ liman ’ , ’ i z m i r ’ => 232 }
7
12
d . keys
p(d . keys )
# => [ " ankara " , " i z m i r " , " k a r s " ]
d . values
p(d . values )
# => [ " k e n t " , " liman " , " hudut " ]
e . keys
p( e . keys )
# => [ " ab " , " cd " , " i z m i r " ]
e . values
p( e . values )
# => [ 1 2 , " liman " , 2 3 2 ]
17
22
4
9
d . keys & e . keys
p(d . keys & e . keys )
# => a n a h t a r l a r i ç i n m a n t ı k s a l AND
# => [ " i z m i r " ]
d . keys + e . keys
p(d . keys + e . keys )
izmir " ]
# => a n a h t a r l a r ı n kümesel b i l e ş i m i
# => [ " ankara " , " i z m i r " , " k a r s " , " ab " , " cd " , "
d . keys − e . keys
p(d . keys − e . keys )
# => a n a h t a r l a r ı n kümesel f a r k ı
# => [ " ankara " , " k a r s " ]
/∗ ∗
ruby hash01 . rb
[ " ankara " , " i z m i r " , " k a r s " ]
[ " k e n t " , " liman " , " hudut " ]
[ " ab " , " cd " , " i z m i r " ]
[ 1 2 , " liman " , 2 3 2 ]
[ " izmir " ]
[ " ankara " , " i z m i r " , " k a r s " , " ab " , " cd " , " i z m i r " ]
[ " ankara " , " k a r s " ]
∗/
Her bir kodun karşısında verdiği sonuç yazılı olduğu için, başka açıklamaya
gerek yoktur.
d = { " ankara " => ’ k e n t ’ , " i z m i r " => ’ liman ’ , " k a r s " => ’ hudut ’ }
hash ambarı verilmiş olsun. Aşağıdaki metotlar, karşılarında belirtilen eylemleri yaparlar
d.methods : d nesnesine uygulanabilecek metotları listeler.
d.length : d ambarındaki öğe sayısını verir.
d . length
# => 3
a.inspect : a ambarın içini gösterir. to_s gibidir.
180
BÖLÜM 14. HASH
d. inspect
=> " { \ " ankara \"=>\" k e n t \ " , \ " i z m i r \"=>\" liman \ " , \ " k a r s \"=>\"
hudut \ " } "
to_s : inspect metodunun yaptığı işi yapar.
d . to_s
=> " { \ " ankara \"=>\" k e n t \ " , \ " i z m i r \"=>\" liman \ " , \ " k a r s \"=>\"
hudut \ " } "
d.class: d nesnesinin hangi sınıftan türetildiğini belirtir.
d. class
# => Hash
d.clear : d ambarındaki bütün öğeleri siler; boş ambar yapar.
d. clear
\{ \}
d . length
# => 0
d.keys : d ambarının anahtarlarını bir array biçiminde verir.
d . keys
# => [ " ankara " , " i z m i r " , " k a r s " ]
d.values : d ambarının değerlerini bir array biçiminde verir.
d . values
# => [ " k e n t " , " liman " , " hudut " ]
d.get(key) Argüman olarak yazılan değere eşleşen anahtarı verir. Eşleşen
değer yoksa hiç (nil) verir.
d . key ( ’ k e n t ’ ) # => " ankara "
d . key ( 7 )
# => n i l
hsh[key –> vlue] Verilen anahtara karşılık gelen değeri verir.
3
h = { " a " => 1 0 0 , " b " => 200 }
h[ "a" ]
#=> 100
h[ "c" ]
#=> n i l
== İki hash ambarının eşit olup olmadığını denetler.
2
7
h1
h2
h3
h4
h1
h2
h3
= { " a " => 1 , " c " => 2 }
= { 7 => 3 5 , " c " => 2 , " a " => 1 }
= { " a " => 1 , " c " => 2 , 7 => 35 }
= { " a " => 1 , " d " => 2 , " f " => 35 }
== h2
#=> f a l s e
== h3
#=> t r u e
== h4
#=> f a l s e
hsh[key –> value] Verilen anahtara karşılık gelen değeri verir.
14.5. SÖZLÜK YAPISININ METOTLARI
3
181
h = { " a " => 1 0 0 , " b " => 200 }
h[ "a" ]
#=> 100
h[ "c" ]
#=> n i l
delete(anahtar) –> vlue Verilen anahtara karşılık gelen değeri verir.
2
h = { " a " => 1 0 0 , " b " => 200 }
h. delete ( "a" )
h. delete ( "z" )
h . d e l e t e ( " z " ) { | e l | "#{ e l } not found " }
#=> 100
#=> n i l
#=> " z not found "
delete_if{|key,value| block} : bloktakine eşleşen bütün anahtar-değer
çiftlerini siler.
1
h = { " a " => 1 0 0 , " b " => 2 0 0 , " c " => 300 }
h . d e l e t e _ i f { | key , v a l u e | key >= " b " }
#=> { " a"=>100}
each : bloktakine eşleşen bütün anahtar-değer çiftlerini verir.
3
h = { " a " => 1 0 0 , " b " => 200 }
h . each { | key , v a l u e | p u t s "#{key } i s #{v a l u e } " }
a i s 100
b i s 200
each_key{|anahtar| blok} : Bloktakine eşleşen bütün anahtarı verir.
1
h = { " a " => 1 0 0 , " b " => 200 }
h . each_key { | key | p u t s key }
a
b
each_value{|value| blok} : Bloktakine eşleşen bütün anahtarı verir.
1
h = { " a " => 1 0 0 , " b " => 200 }
h . each_value { | v a l u e | p u t s v a l u e }
100
200
empty? : Ambar boş ise true verir.
1
{ } . empty ?
#=> t r u e
fetch(anahtar) : Anahtara karşılık gelen değeri verir
h = { " a " => 1 0 0 , " b " => 200 }
h. fetch ( "a" )
h . f e t c h ( " z " , " go f i s h " )
#=> 100
#=> " go f i s h "
has_key?(anahtar) : Verilen anahtar ambarda ise true verir.
2
h = { " a " => 1 0 0 , " b " => 200 }
h . has_key ? ( " a " )
#=> t r u e
h . has_key ? ( " z " )
#=> f a l s e
182
BÖLÜM 14. HASH
include?(anahtar) : Verilen anahtar ambarda ise true verir. has_key metoduna denktir
2
h = { " a " => 1 0 0 , " b " => 200 }
h . include ?( " a " )
#=> t r u e
h . include ?( " z " )
#=> f a l s e
has_value?(değer) : Verilen değer ambarda ise true verir.
2
h = { " a " => 1 0 0 , " b " => 200 }
h . has_value ? ( 1 0 0 )
#=> t r u e
h . has_value ? ( 9 9 9 )
#=> f a l s e
invert : Bir bölükte anahtarları değer,değerleri anahtar yaparak yeni bir
sözlük yaratır.
2
h = { " n " => 1 0 0 , "m" => 1 0 0 , " y " => 3 0 0 , " d " => 2 0 0 , " a " => 0 }
h. invert
#=> {0=>"a " , 100=>"m" , 200=>"d " , 300=>"y " }
keys : Bir sölükte anahtarlardan oluşan bir array yaratır.
h = { " a " => 1 0 0 , " b " => 2 0 0 , " c " => 3 0 0 , " d " => 400 }
h . keys
#=> [ " a " , " b " , " c " , " d " ]