Merhaba arkadaşlar ,
mobilhanem.com sitemiz için anlattığımız/yayınladığımız Swift Dersleri serimize bu yazımızda Swift Queue Kullanımı ile devam edeceğiz. Bir önceki yazımızda Swift Stack Kullanımı konusundan bahsetmiştik. Bu derse başlamadan önce xcode’u kurmak ve playground üzerinde swift kodu yazabilmek için bu dersimizi okumanızı tavsiye ederim.
Swift Queue Nedir ?
Queue kelime karşılığı olarak kuyruk anlamına gelmektedir. Queue ‘yu tanımlarken bir önceki dersimizde yaptığımız gibi Struct yapısını ve Array ‘leri kullanacağız. Queue ‘yu marketlerde veya başka yerlerde girdiğimiz kuyruklar gibi düşünebiliriz. Markette alışverişiniz bittikten sonra aldıklarınızı ödemek için sıraya girdiğinizi düşünün. Sıraya ne kadar erken girerseniz o kadar erken ödeme işlemini yapıp evinize gidebilirsiniz. Yani burada ” ilk giren ilk çıkar ” mantığı çalışmaktadır. Buna FIFO yani first in first out denir. Burada olan bir kaç durum vardır. Bunlardan biri sizin sıraya girme (Enqueue) işleminiz , sizin sıradan çıkma(Dequeue) işleminiz , sıranın en başı(Front) ve sıranın en sonu(Rear) bunlardan bazılarıdır. Sıraya eleman ekleyebilir ve eleman çıkarabilirsiniz.
Enqueue, Dequeue, Front ve Rear Nedir ?
İlk olarak Enqueue nedir ona bakalım. Enqueue yukarıda anlattığım , sizin sıraya yani kuyruğa girme işleminizdir. Sıraya girdiğiniz anda siz en sonda olmuş olursunuz ve buda Rear durumudur yani en sondaki elemandır.Ve sizin sıranın en başında olma durumunuz vardır yani sıranın size gelmiş olması bu durumda ise Front durumudur. Sizin kuyruktan çıkma yani kasadan parayı ödeyip çıkmanız ise Dequeue işlemidir. Şimdi bu anlatılanları aşağıda hazırlamış olduğum görsel üzerinden pekiştirebilirsiniz.
Swift Enqueue Fonksiyonu Nedir?
Swift Queue ‘da elemanlar sıralı olarak tutulmaktadır. Elimizde boş bir Queue olduğunu düşünelim. Eleman eklediğiniz zaman eklediğiniz eleman en son sıraya gelir. Ve Queue ‘nun Rear elemanı eklediğiniz o eleman olur ve aynı zamanda Queue içinde başka eleman olmadığı için Front elemanıda olmuş olur. Tekrar bir eleman eklediğimizde ise ilk eklediğimiz eleman Front elemanı yani ilk sıradaki elemanı olmuş olur. En son eklediğimiz eleman ise Rear elemanı yani en sondaki eleman olmuş olur. Enequeue fonksiyonu aynı push fonksiyonu gibi çalışmaktadır.
Swift Dequeue Fonksiyonu Nedir?
Yukarıda anlattığım ve 2 adet eleman eklediğimiz Queue üzerinde işlem yaptığımızı düşünelim. Dequeue fonksiyonu aynı pop fonksiyonu gibi çalışmaktadır. Dequeue fonksiyonu input olarak değer almaz. Çağrıldığı anda Queue ‘nun Front elemanını yani ilk eklenmiş elemanını Queue içerisinden çıkarır.
Swift Queue Oluşturma
Swift dilinde Queue oluşturmak için Struct kullanabiliriz. Struct yerine aynı işi Class oluşturarakta yapabiliriz.
struct Queue<T> { }
Queue isimli Struct ‘ımızı oluşturduk. Burada <T> yazmamızın sebebi oluşturduğumuz bir önceki dersimizde de anlattığım gibi Queue yapısının bütün veri tiplerini desteklemesi içindir. Queue yapısını oluştururken Array ‘lerdende faydalanacağız. Şimdide Struct ‘ımıza kullanacağımız Array ‘imizi tanımlayalım. Bu Array ‘i tanımlamamızın sebebi dequeue ve enqueue işlemlerinde elemanlarımızı ekleyeceğimiz veya çıkaracağımız yer olarak kullanacak olmamızdır. Enqueue fonksiyonunu çağırdığımızda elemanı, oluşturduğumuz Array ‘e ekleyeceğiz ve Dequeue fonksiyonunu çağırdığımızda da yine aynı Array üzerinde eleman çıkarma işlemini gerçekleştireceğiz. Evet şimdi Queue isimli Struct ‘ımızda bu işlemler için kullanacağımız Array ‘i oluşturalım. Fakat oluştururken bütün veri tiplerini desteklemesi için türüne T yazmayı unutmayalım.
struct Queue<T> { var list = [T]() }
Enqueue Fonksiyonu
list isimli Array ‘imizi oluşturduk. Şimdi ise list Array ‘imize eleman eklememizi sağlayacak olan Enqueue fonksiyonumuzu yazalım.
struct Queue<T> { var list = [T]() mutating func enqueue(_ deger: T) { list.append(deger) } }
Fonksiyonumuzun ismini enqueue olarak yazdıktan sonra input olarak generic type yani her türlü değişken tipini alabilen T olarak gösterilen ve aldığı o değere fonksiyonun içinde kullanmak için isim olarak deger ismini veren fonksiyonumuzu yazdık. Fonksiyonumuzun içerisinde önceden oluşturmuş olduğumuz list isimli array ‘imize daha öncedende yaptığımız append fonksiyonu ile input olarak eklemek istediğimiz değeri vererek ekleme işlemini gerçekleştiriyoruz.
Dequeue Fonksiyonu
Ve şimdi sıra dequeue işlemine yani Queue ‘muzda bulunan elemanları çıkarmaya. Hemen dequeue işlemini yapacak fonksiyonumuzu yazmaya başlayalım,
struct Queue<T> { var list = [T]() mutating func enqueue(_ deger: T) { list.append(deger) } mutating func dequeue() -> T? { if !list.isEmpty { return list.removeFirst() } else { return nil } } }
Evet dequeue işlemini yani kuyruktan çıkarma işlemini yapmak için dequeue isimli fonksiyonumuzu yazdık. Bu fonksiyonumuz tıpkı Stack Kullanımı dersinde anlattığımız pop fonksiyonu gibi input olarak değer almayacaktır. Bunun yerine geriye T? türünde (yani yazdığımız Queue Struct ‘tan oluşturacağımız Queue ‘nun tipi olarak ne belirticeksek o. Int türünde Queue oluşturacaksak dönüş tipide Int olacaktır. String türünde oluşturursak String türünde olacaktır) geriye değer döndürecektir.
Queue Boş Olduğunda Dequeue İşlemi
Bir önceki derste (Swift Stack Kullanımı) Stack içerisinde bulunan eleman sayısından daha fazla pop işlemi yaparsak hata ile karşılaşacağınızı söylemiştim. Bunun çözümü ise aşağıda anlattığım gibidir . Aşağıdaki işlemlerin aynısını Stack yapısınada uygulayarak sorununuzu çözebilirsiniz. Bir başka yaklaşım olarakta daha öncedende kullandığımız if let yapısını da kullanadabilirsiniz. Aşağıdaki yöntem eğer Queue ‘muz boş ise dequeue işlemini yaptırmıyor ve hatayı önlemiş oluyor.
Burada queue ‘muzun boş olma durumuda olacağı için bu durumda fonksiyonumuzun ne yapmasını söylememiz gerekmektedir. Aksi taktirde boş bir queue ‘da dequeue işlemini yapmaya çalıştığımızda hata ile karşılaşırız. Bunun içinde basit bir if else yapısını kullanarak kontrol edebiliriz. Peki !list.isEmpty ne demek bu burada ne yapıyor ?
Daha öncedende yazılarımda da belirttiğim gibi isEmpty başındaki dizi , dictionary , set gibi yapıların boş olup olmamasını kontrol ederek geriye true veya false değerleri döndürür. Eğer doluysa false , boş ise true değerlerini döndürür. Bir Boolean değerin başına ! sembolünü eklersek tersini elde etmiş oluruz. Yani (!true) bize false değerini verir. Peki neden ilk olarak boş olma durumunu ele alıp ardından else ile dolu olduğu durumu ele almadıkta böyle bir şey yaptık ? Sonuçta şu şekildede yazabilirdik,
mutating func dequeue() -> T? { if list.isEmpty { return nil } else { return list.removeFirst() } }
Sizinde gözünüzü tırmaladı mı bilmiyorum ama benimkini biraz tırmaladı açıkçası 🙂 Yani tamamen kişisel bir karar. Kodu yazan kişinin zevkine ve alışkanlıklarına bağlı bir şey 🙂
Bizde dolu olduğu durumda list isimli Array ‘imizden ilk eklenen değeri çıkaracağımız için removeFirst fonksiyonu ile ilk eklenen yani ilk elemanı çıkarmış olduk ve return keyword ‘ünün yanına yazdığımız için hem çıkarıp hemde geriye döndürmüş olduk. Bunu şu şekildede yapabilirdim fakat daha fazla kod ve zaman harcayacağım için yapmadım ,
mutating func dequeue() -> T? { print(list.isEmpty) if !list.isEmpty { let cikarmaIslemiYapılmisDizi = list.removeFirst() return cikarmaIslemiYapılmisDizi } else { return nil } }
Daha fazla kod yazmak daha fazla hatanın çıkmasını sağlar.
Evet else bloğumuzda ise yani list Array ‘imiz boş olduğunda, geriye nil değeri döndürmüş olduk ve kodumuzun hata vermesini böylelikle engellemiş olduk .
Queue İlk Eklenmiş Elemanı Bulma (Front)
Şimdide Queue ‘muzun ilk eklenen elemanını bulmak için fonksiyonumuzu yazmaya başlayalım,
struct Queue<T> { var list = [T]() mutating func enqueue(_ deger: T) { list.append(deger) } mutating func dequeue() -> T? { if !list.isEmpty { return list.removeFirst() } else { return nil } } func ilkEklenenEleman() -> T? { if !list.isEmpty { return list.first } else { return nil } } }
ilkEklenenEleman isimli fonksiyonumuzu yazdık. Bu fonksiyonumuz input olarak değer almayıp geriye sadece ilk elemanı döndüren bir fonksiyondur. Burada tekrar belirmek istiyorum, T? yazmamızın sebebi ilerde farklı türlerde Queue ‘lar oluşturabilmek içindir. Burada T? yerine Int yazsaydık sadece oluşturulan queue ‘nun türü Int olduğu zaman işe yarardı. T? yazarak her türü desteklemesini sapladık. Peki neden ? sembolünü koyduk derseniz, her zaman queue ‘nun dolu olmasını bekleyemeyiz. Queue dolu olmadığında yani boş olduğunda, ilk eklenen elemanı olmayacağı için Optional olarak tanımladık yani değer döndüremeyebileceğini belirtmiş olduk.
if ‘in koşulunun nasıl çalıştığını yukarıda anlatmıştım şimdi tekrar değinmeyeceğim. if koşulu sağlandığında yani list Array ‘imiz dolu olduğunda return keyword ‘ü ile geriye list Array ‘inin ilk elemanını .first diyerek döndürüyoruz. Eğer list Array ‘imiz boş ise else bloğu çalışacak ve kodumuz hata vermeden işleyişine devam edecektir.
Önemli bir not , burada .first ile Array ‘in ilk elemanına eriştiğimizde Complexity ‘si O(1) olur. Bir önceki dersimizde dizinin son elemanına index ‘i ile erişmiştik. Bunun dışında .last ilede son elemanına erişebilirdik.
Queue ‘nun Boş Olup Olmadığını Bulma
Şimdi de bir önceki dersimde anlattığım Computed Property ile Queue ‘muzun boş olup olmadığını bize gösterebilecek bir fonksiyon yazalım,
struct Queue<T> { var list = [T]() mutating func enqueue(_ deger: T) { list.append(deger) } mutating func dequeue() -> T? { if !list.isEmpty { return list.removeFirst() } else { return nil } } func ilkEklenenEleman() -> T? { if !list.isEmpty { return list.first } else { return nil } } var isEmpty: Bool { return list.isEmpty } }
Computed Property olarak oluşturduğum isEmpty , Boolean türünde değer döndürecektir. Neden bunu yapıyoruz diye düşünebilirsiniz. Neden direkt Array ‘lerde yaptığımız gibi isEmpty fonksiyonu ile yapmadık diyebilirsiniz. Daha sonra oluşturacağımız dequeue , enqueue işlemlerini yapacağımız queue ‘muz direkt olarak isEmpty fonksiyonuna erişemez. Çünkü isEmpty fonksiyonu array ‘ler, dictionary ‘ler, set ‘ler gibi collection type ‘lar içindir. Ama burada oluşturacağımız queue ‘muz bizim kendi hazırladığımız (Structure yapısında) bir collection type olacaktır ve bunu kendimiz hazırladığımız için isEmpty fonksiyonunu bizim kendimiz yazmamız gerekmektedir hazır olarak diğer collection type ‘lardaki gibi kullanamayız. Fonksiyonun yazılması ise yukarıda anlattıklarımdan çok daha basit 🙂
return keyword ‘ü ile list.isEmpty diyerek list Array ‘imizin isEmpty fonksiyonuna erişiyoruz.Ve list Array ‘imizin boş olup olmadığını kontrol ediyoruz ve isEmpty fonksiyonundan geriye dönen değeri kendi yazdığımız isEmpty fonksiyonuna dönüş değeri olarak veriyoruz. list isimli Array ‘imiz boş ise bizim queue ‘muzda otomatik olarak boş olacaktır.
Aslında burada Queue Struct ‘ımıza kendimiz isEmpty isimli Computed Property ekliyoruz fakat yapımızın içerisinde oluşturduğumuz list isimli Array ‘in kendi isEmpty fonksiyonuna erişimi olduğu için tekrar Array ‘in boş olup olmadığını kontrol eden kodları yazmamış oluyoruz. Ve Array ‘in isEmpty fonksiyonundan dönen değeri Computed Property ‘mizin içerisinde return değeri olarak vermiş oluyoruz. Burada Computed Property yerine Fonksiyon olarakta oluşturabilirdik.
Swift Boş Queue Oluşturma
Evet yukarda o kadar uğraştığımız kodların meyvesini yeme zamanı 🙂
Hemen boş bir queue oluşturalım,
Stack Kullanımı dersinde ve genel olarak verdiğim aynı örnek üzerinden gidiyorum ki farklılıkları ve benzerlikleri daha çok göze çarpsın. Eğer farklı örnekler isterseniz yorum kısmından belirtebilirsiniz.
var selamlama = Queue<String>()
selamlama isimli Queue ‘muzu String değerler alabilecek şekilde oluşturduk. Şu an selamlama Queue ‘muzun içinde bir değer yoktur. Queue ‘muzu oluşturduktan sonra selamlama Queue ‘muz şu şekildedir,
Hemen bunu kontrol edebiliriz,
print(selamlama.isEmpty)
selamlama Queue ‘muzun isEmpty isimli bir property ‘si vardır. Yukarıda bunu biz kendimiz yazmıştık. selamlama.isEmpty diyerek boş olup olmadığını öğrenip dönen cevabıda print komutu ile konsola bastırmış olduk. Burada selamlama ‘nın içerisinde eleman olmadığı için konsolda true cevabını görmeniz gerekmektedir.
Queue ‘ya Eleman Ekleme (Enqueue)
Şimdide selamlama Queue ‘muza “Merhaba” değerimizi ekleyelim,
selamlama.enqueue("Merhaba")
Queue ‘muzun ismini yazdıktan sonra yazdığımız enqueue isimli fonksiyonu çağırıyoruz ve input olarak “Merhaba” değerini veriyoruz. Bu işlemi birde şekil üzerinde görelim,
enqueue fonksiyonu bittiğinde ise selamlama Queue ‘muz şu şekilde gözükecektir,
Ve şimdi de selamlama Queue ‘muza “Benim” değerini girelim,
Şimdi de enqueue işlemi bittikten sonraki selamlama Queue ‘muzun durumuna bakalım,
Evet şimdi ise kalan diğer iki değerimizi girelim ve son durumumuza bakalım ,
selamlama.enqueue("Adım") selamlama.enqueue("Taha")
Enqueue işlemlerimizi bitirdikten sonra şimdi de Dequeue işlemimize göz atalım. Dequeue işlemini yapmak için tanımladığımız dequeue fonksiyonundan faydalanacağız. dequeue fonksiyonu input olarak değer almamaktaydı bunun yerine geriye queue ‘muzu döndürmekteydi. Bu işlemi yapmaya başlamadan önce çok kısa olarak ilk eklenen elemanı bulalım. Bu içelim yapmak için ise daha önceden yazdığımız ilkEklenenEleman fonksiyonundan faydalanacağız,
selamlama.ilkEklenenEleman()
Sadece bu kadar ve ilk eklenen elemanımızın ne olduğunu görebildik. Bu satır çalıştırıldıktan sonra playground üzerinde sağ tarafta “Merhaba” değerini görmüş olmalısınız. Eğer göremediyseniz print komutu ile konsola bastırabilirsiniz.
Queue ‘dan Eleman Çıkarma (Dequeue)
Dequeue fonksiyonumuzu bir kere çağıralım ve ardından ilk eklenen elemana bakalım hala “Merhaba” değerini mi vericek yoksa “Benim” değerini mi?
selamlama.dequeue()
dequeue fonksiyonumuzu çağırdık. Yapılan işlemleri birde şekil üzerinde görelim,
Dequeue işlemi bittikten sonraki selamlama Queue ‘muzun sonucunu görelim,
Evet dequeue fonksiyonumuz çalıştırıldıktan sonra ilk eklenen eleman konumuna (Front) “Benim” değeri geldi şimdi tekrar ilkEklenenEleman fonksiyonumuzu çağırarak doğru çalışıp çalışmadığını kontrol edelim. Çıktı olarak bize “Benim” değerini vermesi lazım.
selamlama.ilkEklenenEleman()
Playground üzerinde sağ tarafta “Benim” değerini gördük ve doğru çalıştığınıda kanıtlamış olduk. Peki selamlama Queue ‘muzun bütün elemanlarını çıkarırsak ve ardından ilkEklenenEleman fonksiyonumuzu çağırırsak hata verir mi ? Onu kontrol edelim, fonksiyonumuzu yazarken hata vermemesi için if else bloğu kullanarak önlemeye çalışmıştık şimdi de deneyip görelim,
İlk önce şu an selamlama Queue ‘muzda 3 adet eleman bulunmaktadır. 3 kere dequeue fonksiyonunu çağırınca içinde eleman kalmayacaktır,
selamlama.dequeue() selamlama.dequeue() selamlama.dequeue()
Evet şimdi selamlama Queue ‘muz boşaldı. Bu durumu yazdığımız isEmpty fonksiyonu ile kontrol edelim ,
selamlama.isEmpty
Ve isEmpty fonksiyonumuz bize true değerini döndürdü buda demek oluyor ki fonksiyonumuz düzgün çalışıyor. Şimdi de boş Queue ‘muzdan bir eleman daha çıkaralım ve neler olacak görelim ,
selamlama.dequeue()
Evet dequeue fonksiyonumuz çağırınca hata vermedi ve geriye nil değerini döndürdü. Fonksiyonumuzu yazarken kullandığımız boş olma durumundaki else bloğu çalıştı ve geriye nil değerini döndürdü ve hata vermemiş oldu. Geriye nil yerine başka değerlerde döndürebiliriz örneğin konsola “bu queue boştur dequeue işlemini yapamazsınız” tarzında bir uyarı gösterebilirsiniz.
Swift Queue Kullanımı dersimizin sonuna geldik. Umarım buraya kadar anlattığım konularla ilgili bir sorununuz yoktur. Takıldığınız yerleri yorum kısmından yazabilirsiniz.
print(“The question of whether a computer can think is no more interesting than the question of whether a submarine can swim”)
print(“Edsger Dijkstra”)
4
hocam mükemmel anlatımlar,
şu iki konu iki kez yazılmış ana swift menüsüne bilginize,
teşekkürler, iyi çalışmalar
Swift Stack Kullanımı
Swift Queue Nedir ?
Şu aşağıdaki kullanım daha temiz 🙂
“`swift
mutating func dequeue() -> T? {
if list.isEmpty { return nil }
return list.removeLast()
}
“`