Ruby'de Set, aynı öğeyi yalnızca bir kez tutan, sırası önemli olmayan bir koleksiyondur. Set[1, 2, 3] ya da Set.new(dizi) ile oluşturulur, add veya << ile öğe eklenir, include? ile üyelik sorgulanır. Birleşim için | (union), kesişim için & (intersection), fark için - (difference), alt küme kontrolü için subset? kullanılır. Ruby 4.0 ile Set çekirdek sınıf oldu; require "set" yazmanız gerekmez.
Set belgesi sınıfı, dizilerin kolay kullanımıyla hash'lerin hızlı aramasını birleştiren bir yapı olarak tanımlar; içeride yalnızca anahtarları olan bir hash'e benzer bir veri yapısı kullanılır. Aşağıdaki örnekleri Ruby 4.0.9 ile çalıştırdık. Dizi ve hash tarafı için Ruby Array metotları ve Ruby Hash metotları yazılarına, dilin geneli için Ruby öğrenme rehberi sayfasına bakabilirsiniz.
Ruby 4.0'da Set'te neler değişti?

NEWS dosyası Set için şu değişiklikleri listeliyor:
Set, otomatik yüklenen bir standart kütüphane sınıfı olmaktan çıkıp çekirdek sınıf oldu.Set#inspect, dizi literaline benzeyen sade bir görünüme geçti:#<Set: {1, 2, 3}>yerineSet[1, 2, 3].Set#to_setveEnumerable#to_setmetotlarına argüman vermek kullanımdan kaldırılma sürecine girdi.set/sorted_set.rbsilindi veSortedSetartık otomatik yüklenmiyor; gerekirsesorted_setgem'ini kuruprequire "sorted_set"yazmanız gerekiyor.Range#to_setsonsuz aralıklar için boyut kontrolü yapıyor.- C eklentileri için bir Set C API'si eklendi (
rb_set_new,rb_set_addgibi).
Set belgesi ayrıca sınıfın 4.0 için C ile yeniden yazıldığını ve bazı çekirdek metotların davranışının ayarlandığını belirtiyor. Bu değişikliğin alt sınıflar üzerindeki etkisini aşağıda ayrı başlıkta gösteriyoruz.
Set oluşturmak
En kısa yol Set[] yazımıdır. Set.new herhangi bir Enumerable alır ve isteğe bağlı bir blokla öğeleri eklemeden önce dönüştürebilir. Diziler ve diğer Enumerable nesneler to_set ile de kümeye çevrilir. Yinelenen öğeler kendiliğinden atılır:
Set[3, 1, 2, 3] # => Set[3, 1, 2]
Set.new([1, 2, 1]) # => Set[1, 2]
Set.new(1..4) { |x| x * x } # => Set[1, 4, 9, 16]
[:a, :b, :a].to_set # => Set[:a, :b]
Set.new # => Set[]
Set.instance_method(:add).owner # => SetÖğe eşitliği eql? ve hash metotlarıyla belirlenir; bu yüzden 1 ile 1.0 farklı öğe sayılır. Belge, bir metin öğe olarak saklanırken, metin zaten dondurulmuş değilse dondurulmuş bir kopyasının saklandığını da belirtiyor.
Öğe eklemek, silmek ve sorgulamak
add ve takma adı << öğeyi ekleyip kümenin kendisini döndürür; öğe zaten varsa hiçbir şey değişmez. Eklemenin gerçekleşip gerçekleşmediğini bilmek istiyorsanız add? kullanın: öğe zaten varsa nil döner. Silmek için delete ve delete? aynı mantıkla çalışır:
etiketler = Set["ruby"]
etiketler << "json"
etiketler.add("ruby")
etiketler # => Set["ruby", "json"]
etiketler.add?("ruby") # => nil
etiketler.add?("set") # => Set["ruby", "json", "set"]
etiketler.size # => 3
etiketler.include?("json") # => true
etiketler.member?("python") # => false
etiketler.delete("set") # => Set["ruby", "json"]include? metodunun takma adlarından biri === olduğu için bir kümeyi doğrudan case ifadesinin when dalında kullanabilirsiniz:
case "ruby"
when etiketler then puts "etiket var"
end
# çıktı: etiket varBelgeye göre kümede saklanan bir öğenin kimliği değişmemelidir; bir öğeyi küme içindeyken değiştirirseniz küme güvenilmez duruma düşer. Böyle bir durumda reset metodu iç durumu yeniler. case yazımı için Ruby if, unless ve case yazısına bakın.
Küme işlemleri: birleşim, kesişim, fark

Küme işlemleri yeni bir küme döndürür ve sağ taraf olarak dizi gibi herhangi bir Enumerable kabul eder:
| Operatör | Metot adı | Sonuç |
|---|---|---|
| | veya + | union | İki taraftaki tüm öğeler |
| & | intersection | Yalnızca ortak öğeler |
| - | difference | Soldaki öğelerden sağdakiler çıkarılmış hali |
| ^ | yok | Ortak olanlar dışındaki tüm öğeler |
a = Set[1, 2, 3, 4]
b = Set[3, 4, 5]
a | b # => Set[1, 2, 3, 4, 5]
a.union(b) # => Set[1, 2, 3, 4, 5]
a & b # => Set[3, 4]
a.intersection([4, 9]) # => Set[4]
a - b # => Set[1, 2]
a.difference(b) # => Set[1, 2]
a ^ b # => Set[1, 2, 5]
a + [9] # => Set[1, 2, 3, 4, 9]Kümeyi yerinde değiştirmek istiyorsanız yeni küme döndüren bu operatörler yerine merge (ekleme) ve subtract (çıkarma) metotlarını kullanın.
Pratik örnek: iki günün ziyaretçileri
Küme işlemleri, iki listeyi karşılaştırma işlerini tek satıra indirir. Aşağıda iki günün ziyaretçi listesinden her iki gün gelenleri, yalnızca ikinci gün gelenleri ve toplam tekil ziyaretçi sayısını buluyoruz. Son satırda add? metodunun nil dönüşünden yararlanarak bir listede tekrar eden öğeleri ayıklıyoruz:
pazartesi = Set["ali", "ayşe", "can"]
sali = ["can", "deniz", "ali", "can"].to_set
pazartesi & sali # => Set["can", "ali"]
sali - pazartesi # => Set["deniz"]
(pazartesi | sali).size # => 4
gorulen = Set.new
%w[a b a c b a].select { |x| !gorulen.add?(x) } # => ["a", "b", "a"]Aynı işi dizilerle de yapabilirsiniz, ancak üyelik sorgusu çok sayıda tekrarlandığında kümenin hash tabanlı yapısı belge tarafından hızlı arama için tasarlanmış bir yöntem olarak öne çıkarılıyor.
Alt küme, üst küme ve karşılaştırma
subset? (takma adı <=) soldaki kümenin tüm öğelerinin sağda olup olmadığını sorar. proper_subset? (<) ayrıca iki kümenin eşit olmamasını ister. superset? ve proper_superset? ters yöndeki karşılıklarıdır. disjoint? ortak öğe yoksa, intersect? en az bir ortak öğe varsa true döner:
kucuk = Set[1, 2]
buyuk = Set[1, 2, 3]
kucuk.subset?(buyuk) # => true
kucuk <= buyuk # => true
kucuk < buyuk # => true
buyuk.superset?(kucuk) # => true
kucuk.proper_subset?(Set[1, 2]) # => false
kucuk.disjoint?(Set[7, 8]) # => true
kucuk.intersect?([2, 9]) # => true
kucuk <=> buyuk # => -1
Set[1, 2] <=> Set[1, 3] # => nil
Set[1, 2] == Set[2, 1] # => true<=> operatörü alt küme ilişkisini yansıtır; iki kümenin birbirinde olmayan öğeleri varsa karşılaştırılamaz ve nil döner. Eşitlikte sıra önemli değildir.
Enumerable metotları ve dönüşümler
Set, Enumerable modülünü içerir ve getirme ile dolaşma için büyük ölçüde bu modülün metotlarına dayanır. Dikkat edilecek nokta şu: select ve map gibi Enumerable metotları dizi döndürür. Sonucu yine küme olarak istiyorsanız to_set ekleyin ya da yerinde değiştiren select! ve map! metotlarını kullanın:
s = Set[1, 2, 3, 4, 5, 6]
s.select(&:even?) # => [2, 4, 6]
s.map { it * 10 } # => [10, 20, 30, 40, 50, 60]
s.to_a # => [1, 2, 3, 4, 5, 6]
s.sum # => 21
s.min # => 1
s.classify(&:odd?) # => {true => Set[1, 3, 5], false => Set[2, 4, 6]}classify, bloğun döndürdüğü değere göre öğeleri kümelere ayırıp bir hash döndürür. Enumerable metotlarının geneli için Ruby Enumerable: map, select, reduce yazısına bakın.
to_set argümanları ve sonsuz aralıklar
Ruby 4.0'da to_set metoduna argüman vermek kullanımdan kaldırılma sürecinde. Ruby kullanımdan kaldırma uyarılarını varsayılan olarak gizlediği için denememizde Warning[:deprecated] = true ile açtık. Sonsuz bir aralığı kümeye çevirmeye çalışmak da artık hemen hata veriyor:
Warning[:deprecated] = true
[1, 2].to_set(Set) # => Set[1, 2]
# stderr: warning: passing arguments to Enumerable#to_set is deprecated
begin
(1..).to_set
rescue => e
puts "#{e.class}: #{e.message}"
end
# çıktı: RangeError: cannot convert endless range to a setAralıklar hakkında daha fazlası için Ruby Range aralıklar yazısına bakabilirsiniz.
Set sınıfından türetmek
Set belgesine göre geriye dönük uyumluluk için, Set sınıfından türeyen sınıflara otomatik olarak Set::SubclassCompatible modülü eklenir. Bu modül alt sınıfın davranışını ve iç metot adlarını 3.4'teki haline yaklaştırır. Farkı inspect çıktısında görebilirsiniz. Yeni kodda uyumluluk katmanına ihtiyacınız yoksa belge Set::CoreSet sınıfından türetmeyi öneriyor:
class EskiSet < Set; end
EskiSet[1, 2] # => #<EskiSet: {1, 2}>
class YeniSet < Set::CoreSet; end
YeniSet[1, 2] # => YeniSet[1, 2]SortedSet artık yok
Ruby 4.0'da SortedSet otomatik yüklenmiyor. Eski kodunuzda geçiyorsa şu hatayı alırsınız:
begin
SortedSet
rescue NameError => e
puts "#{e.class}: #{e.message}"
end
# çıktı: NameError: uninitialized constant SortedSetÇözüm, NEWS dosyasının önerdiği gibi sorted_set gem'ini kurmak ya da sıralı görünüme yalnızca çıktı alırken ihtiyacınız varsa set.sort ile sıralı bir dizi almaktır.
Sık yapılan hatalar

- Ruby 4.0'da
inspectçıktısının değiştiğini hesaba katmadan, eski#<Set: {...}>biçimine bakan testler yazmak. selectveyamapsonucunun hâlâ küme olduğunu varsaymak; dizi döner.- Kümeye eklediğiniz değiştirilebilir bir nesneyi (örneğin bir diziyi) sonradan değiştirmek.
- Sıra gerektiren işlerde kümeye güvenmek; sıralı sonuç için
sortkullanın. to_setmetoduna sınıf argümanı vermeye devam etmek; bu kullanım kaldırılma sürecinde.
Sık Sorulan Sorular
Set mi Array#uniq mi kullanmalıyım?
Bir kez tekrarları temizleyip diziyle devam edecekseniz uniq yeterli. Sürekli üyelik sorgusu yapacak ve öğe ekleyip çıkaracaksanız, hash'e benzer yapısıyla hızlı arama yapan Set daha uygundur.
Ruby 4.0'da require "set" yazarsam sorun olur mu?
Set artık çekirdek sınıf olduğu için gerek yok. Eski sürümleri de desteklemesi gereken kodlarda bu satırı tutabilirsiniz; bu yazıdaki örneklerde hiç kullanmadık.
Set öğeleri sıralı mı tutulur?
Belge Set'i sırasız değerler koleksiyonu olarak tanımlar. Denememizde öğeler ekleme sırasıyla görüntülendi, ancak sıra gereken yerde sort ile açıkça sıralayın.
İki kümenin ortak öğesi var mı diye nasıl bakarım?
a.intersect?(b) en az bir ortak öğe varsa true döner; tersini sormak için a.disjoint?(b) kullanın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Ruby öğrenme rehberi · Ruby Array metotları · Ruby Hash metotları · Ruby Enumerable · Ruby 4.0 yenilikleri · Ruby Range