Kümeler ve Donuk kümeler

Giriş

Graphical Depiction of Sets as Circles

Eğitim materyalimizin bu bölümünde, Python’un kümeleri nasıl ele aldığını göreceğiz. Kümeler günümüzde modern matematiğin ilgi alanına giriyorsa da durum hep böyle değildi. Küme teorisi, pek çok kimse tarafından, bazıları büyük düşünürler olmak üzere, reddedilmişti. Wittgenstein, bu filozoflardan biriydi. O küme teorisini sevmemişti ve matematiğin “küme teorisinin zararlı deyimlerinin istilasına uğramış olması” eleştirisini yapmıştı. Küme teorisinin “deli saçması”, “gülünesi” ve “yanlış” olduğunu söylemişti. Wittgenstein, Alman matematikçi Georg Cantor (küme teorisinin kaşifi) vefatından yıllar sonra bu eleştirileri yapmıştır. David Hilbert, Wittgenstein’in eleştirilerine şu ünlü sözü söyleyerek karşı durmuştur: “Kimse Cantor’un oluşturduğu cennetten bizi çıkaramaz”.

Cantor, "Beiträge zur Begründung der transfiniten Mengenlehre" isimli eserinin başında küme kavramını şöyle tanımlamıştır: “Bir küme kavrama sahamızın ve düşüncemizin içinde kesin ve belirgin nesnelerin bütününün bir araya gelmesidir – bu da kümenin elemanları olarak adlandırılır”. Günümüzde “düz” olarak şöyle diyebiliriz: Bir küme, iyi tanımlanmış bir nesne koleksiyonudur. Bir kümenin elemanları herşey olabilir: sayılar, karakterler, kelimeler, isimler, alfabenin harfleri, ve hatta başka kümeler ve diğerleri. Kümeler genellikle büyük harflerle gösterilir. Bu tam olarak matematiksel bir tanım değildir, ancak aşağıdaki durumlar için yeterince iyidir.

Bir koleksiyon türü olan “küme” (set) veri türü Python’da 2.4 sürümünden beri bulunmaktadır. Bir kümede benzersiz ve değişmez nesnelerin sıralanmamış bir koleksiyonu bulunur. Küme veri türü, adından da anlaşılacağı üzere matematikten bildiğimiz kümelerin Python’a uyarlanmış halidir. Bu da listeler veya demetlerden farklı olarak kümelerde aynı elemandan neden daha fazla olamadığı sorusunu açıklamış oluyor.

Kümeler

Bir küme oluşturmak istiyorsak, bir sıralama ya da başka bir yinelenebilir nesneyi içeren bir gömülü küme fonksiyonunu çağırmamız yeterlidir:

Aşağıdaki örnekte bir kelime dizisi x isimli bir küme oluşturmak üzere karakterlerine ayrıştırılıyor:

In [1]:
x = set("Bir Python Eğitim Materyali")
x
Out[1]:
{' ',
 'B',
 'E',
 'M',
 'P',
 'a',
 'e',
 'h',
 'i',
 'l',
 'm',
 'n',
 'o',
 'r',
 't',
 'y',
 'ğ'}
In [2]:
type(x)
Out[2]:
set

Bir listeyi gömülü küme fonksiyonuna gönderirsek şu cevapla karşılaşırız:

In [3]:
x = set(["Perl", "Python", "Java"])
x
Out[3]:
{'Java', 'Perl', 'Python'}

Şimdi de içinde tekrarlanan öğe bulunan bir demeti küme haline getirelim; örneğimizde “Paris” şehri yineleniyor:

In [4]:
şehirler = set(("Paris", "Lyon", "London","Berlin","Paris","Birmingham"))
şehirler
Out[4]:
{'Berlin', 'Birmingham', 'London', 'Lyon', 'Paris'}

Beklendiği gibi şehir kümesinde tekrarlanan bir eleman yok.

Kümelerin Değiştirilemezliği

Kümeler, değiştirilebilir nesnelere izin vermeyecek şekilde tanımlanmıştır. Aşağıdaki örnekte eleman olarak liste içeremeyeceğimizi anlıyoruz:

In [9]:
şehirler = set((["Python","Perl"], ["Paris", "Berlin", "London"]))
şehirler
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-9a34d1fa9c5f> in <module>
----> 1 cities = set((["Python","Perl"], ["Paris", "Berlin", "London"]))
      2 cities

TypeError: unhashable type: 'list'

Diğer bir taraftan demetlerde böyle bir sorun yoktur:

In [11]:
şehirler = set((("Python","Perl"), ("Paris", "Berlin", "London")))
şehirler
Out[11]:
{('Paris', 'Berlin', 'London'), ('Python', 'Perl')}

Donuk kümeler

Kümeler değiştirilebilir öğeler içeremiyor olsa da, kümelerin kendisi değiştirilebilir:

In [12]:
şehirler = set(["Frankfurt", "Basel","Freiburg"])
şehirler.add("Strasbourg")
şehirler
Out[12]:
{'Basel', 'Frankfurt', 'Freiburg', 'Strasbourg'}

Donuk kümeler ise hiçbir şekilde değişiklik kabul etmez:

In [13]:
şehirler = frozenset(["Frankfurt", "Basel","Freiburg"])
şehirler.add("Strasbourg")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-9abedff8e0b3> in <module>
      1 cities = frozenset(["Frankfurt", "Basel","Freiburg"])
----> 2 cities.add("Strasbourg")

AttributeError: 'frozenset' object has no attribute 'add'

Gelişmiş yazım

Python 2.6’dan bu yana kümeleri gömülü küme fonksiyonu kullanmadan da tanımlayabiliyoruz. Bunun için süslü parantez kullanabiliriz:

In [1]:
sıfatlar = {"ucuz","pahalı","masrafsız","hesaplı"}
sıfatlar
Out[1]:
{'hesaplı', 'masrafsız', 'pahalı', 'ucuz'}

Küme İşlemleri

add(element)

Bu yöntem değiştirilmez olması gereken bir elemanı bir kümeye ekler.

In [2]:
renkler = {"kırmızı","yeşil"}
renkler.add("sarı")
renkler
Out[2]:
{'kırmızı', 'sarı', 'yeşil'}
In [3]:
renkler.add(["siyah","beyaz"])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-d4fe29f513eb> in <module>
----> 1 renkler.add(["siyah","beyaz"])

TypeError: unhashable type: 'list'

Elbette ki yalnızca bir element, kümede yoksa, eklenecektir. Eğer kümenin içinde varsa yöntem çağrısının bir etkisi olmaz.

clear()

Kümenin bütün elemanlarını siler.

In [4]:
şehirler = {"Stuttgart", "Konstanz", "Freiburg"}
şehirler.clear()
şehirler
Out[4]:
set()

copy

Döndürülen bir yüzeysel kopya oluşturur.

In [8]:
başka_şehirler = {"İstanbul","Bolu","Hatay"}
şehirler_yedekleme= başka_şehirler.copy()
başka_şehirler.clear()
şehirler_yedekleme
Out[8]:
{'Bolu', 'Hatay', 'İstanbul'}

Bir atamanın yeterli olabileceğini düşünebilirsiniz:

In [9]:
başka_şehirler = {"İstanbul","Bolu","Hatay"}
şehirler_yedekleme = başka_şehirler
başka_şehirler.clear()
şehirler_backup
Out[9]:
set()

'şehirler_backup = başka_şehirler' ataması yalnızca bir belirteç yani aynı veri yapısına başka bir isimle gönderme oluşturur.

difference()

Bu yöntemle iki veya daha fazla kümenin farkı yeni bir küme olarak alınır.

In [20]:
x = {"a","b","c","d","e"}
y = {"b","c"}
z = {"c","d"}
x.difference(y)
Out[20]:
{'a', 'd', 'e'}
In [21]:
x.difference(y).difference(z)
Out[21]:
{'a', 'e'}

Fark yöntemini kullanmak yerine “-” operatörünü de tercih edebilirsiniz:

In [22]:
x - y
Out[22]:
{'a', 'd', 'e'}
In [23]:
x - y - z
Out[23]:
{'a', 'e'}

difference_update()

difference_update yöntemi birinci küme içinden ikinci kümenin mevcut bütün elemanlarını siler. x.difference_update(y) ile “x =x – y” aynı etkiye sahiptir.

In [24]:
x = {"a","b","c","d","e"}
y = {"b","c"}
x.difference_update(y)
In [25]:
x = {"a","b","c","d","e"}
y = {"b","c"}
x = x - y
x
Out[25]:
{'a', 'd', 'e'}

discard(el)

“el” isimli bir eleman eğer kümede mevcut ise kümeden silinir. Kümede “el” isimli bir eleman yok ise hiçbir şey olmaz.

In [26]:
x = {"a","b","c","d","e"}
x.discard("a")
x
Out[26]:
{'b', 'c', 'd', 'e'}
In [27]:
x.discard("z")
x
Out[27]:
{'b', 'c', 'd', 'e'}

remove(el)

Discard() gibi çalışır, ancak kümede “el” isimli bir eleman yoksa KeyError hatası oluşur.

In [28]:
x = {"a","b","c","d","e"}
x.remove("a")
x
Out[28]:
{'b', 'c', 'd', 'e'}
In [29]:
x.remove("z")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-29-047792990e4d> in <module>
----> 1 x.remove("z")

KeyError: 'z'

union(s)

İki kümenin birleşimini yeni bir küme olarak döndürür.

In [30]:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x.union(y)
Out[30]:
{'a', 'b', 'c', 'd', 'e', 'f', 'g'}

Borulama “|” operatörü ile bu işlem kısaltılabilir:

In [31]:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x | y
Out[31]:
{'a', 'b', 'c', 'd', 'e', 'f', 'g'}

intersection(s)

Örnek küme ve s kümesinin kesişim kümesini yeni bir küme olarak döndürür. Başka bir deyişle, iki kümede ortak olan elemanlar yeni bir kümeye alınır.

In [32]:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x.intersection(y)
Out[32]:
{'c', 'd', 'e'}

Ampersand (&) operatörü bu işlemin kısaltılmasında kullanılır:

In [33]:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x  & y
Out[33]:
{'c', 'd', 'e'}

isdisjoint()

Bu yöntem, iki küme kesişimiyorsa True döndürür.

In [34]:
x = {"a","b","c"}
y = {"c","d","e"}
x.isdisjoint(y)
Out[34]:
False
In [35]:
x = {"a","b","c"}
y = {"d","e","f"}
x.isdisjoint(y)
Out[35]:
True

issubset()

x.issubset(y) yöntemi x, y kümesinin alt kümesi ise True döndürür. “<=” operatörü “alt kümesi” anlamında, “>=” üst kümesi” anlamında kullanılır. “<” ise bir kümenin başka bir kümenin alt kümesi olup olmadığını kontrol etmemize imkân tanır.

In [36]:
x = {"a","b","c","d","e"}
y = {"c","d"}
x.issubset(y)
Out[36]:
False
In [37]:
y.issubset(x)
Out[37]:
True
In [38]:
x < y
Out[38]:
False
In [39]:
y < x # y x’in düzgün bir alt kümesidir
Out[39]:
True
In [40]:
x < x # bir küme asla olduğu gibi başka bir kümenin alt kümesi olamaz.
Out[40]:
False
In [41]:
x <= x 
Out[41]:
True

issuperset()

x.issuperset(y) ifadesi True döndürür, bunun için x’in y’nin bir üst kümesi olması gerekir. “üst küme” ifadesi için kısaltma olarak “>=” kullanılabilir. “>” operatörü, bir kümenin başka bir kümenin düzgün bir üst kümesi olup olmadığını kontrol etmek için kullanılabilir.

In [42]:
x = {"a","b","c","d","e"}
y = {"c","d"}
x.issuperset(y)
Out[42]:
True
In [43]:
x > y
Out[43]:
True
In [44]:
x >= y
Out[44]:
True
In [45]:
x >= x
Out[45]:
True
In [46]:
x > x
Out[46]:
False
In [47]:
x.issuperset(x) 
Out[47]:
True

pop()

pop() yöntemi keyfi olarak bir kümeden eleman siler ve değerini döndürür. Küme boş ise KeyError hatası oluşur.

In [48]:
x = {"a","b","c","d","e"}
x.pop()
Out[48]:
'c'
In [49]:
x.pop()
Out[49]:
'b'