İçeriğe geç

Swift Generics Type Constraints and Where Clauses kullanımı

swift

Merhabalar bu yazımda Swift programlama dilinin modern programlama anlayışında POP programlama ve OOP programlama anlayışlarına büyük katkılar sağlayan Generics kullanımına bir kaç güzel örnek vereceğim.

Generics ile ilgili yazılar yazmıştım ancak genericsin ileri seviye kullanımına örnek vermedim. Bu yazımda Generics’i Advence seviyede nasıl kullanırız bunun örneğini vermiş olacağım.

Generics örnek olarak bir fonksiyon yazdığımızı düşünelim bu fonksiyon Integer değişkenler alır ve bunlar üzerinde işlemler yapar şeklinde yazarsak sadece Integer’la işlem yapan fonksiyon yazmış oluruz. Ancak Generics belirsiz değişken tipiyle fonksiyon,sınıf,structların tipi belli değildir. Siz içerisine ne koyarsanız o şekli alır. Buda büyük kolaylıklar sağlar. Ayrı ayrı fonksiyonlar sınıflar yazmanıza gerek kalmaz.

func generics<T>(x:T) {
    
    print(x)
    
}

generics("X")
generics(3.1)
generics(1231)
generics([1,99,123])
generics(["Point":5])

Basit bir generics örneği gördüğümüz gibi tür farketmeden fonksiyon kullanılmakta.

Aşağıdaki örneğimde FİFO(First in first out) yapısına uygun veri yapısı tipi olan Kuyruk yapısını generics ile tüm türlere uygun şekilde yazmış olduk.

struct Queue<T> {
    
    var arraySet = [T]()
    
    
    mutating func enqueue(element:T) {
        
        arraySet.append(element)
        
    }
    
    
    mutating func dequeue() ->T? {
        
        if arraySet.isEmpty {
            return nil
        } else {
           return arraySet.removeAtIndex(0)
        }

    }
  
}


var nesne = Queue<String>()
nesne.enqueue("A")
nesne.enqueue("B")
nesne.enqueue("C")
nesne.dequeue() // A

Daha sonra generics türü belirlerken Protokolleri kullanabiliyoruz buda generics’i ileri seviyeye çıkarıyor. Bazı durumlarda türlerin uyuşmazlığı vb durumlarda Swift dilinde bulunan protokoller kullanılmakta.

Aşağıdaki verdiğim örnekte dizinin türü farketmeksizin ortadaki değeri döndüren fonksiyon yazmış olduk.

func mid<T:Comparable>(array: [T]) -> T {
    return array.sort()[(array.count - 1) / 2]
}

mid([1,2,3,4,6,7,3,99]) // 3
mid(["A","B","C","D","E"]) // C

Burada T:Comparable belirtmek zorundayız aksi takdirde derleyici uyarı verecektir. Girilen türün sıralanabilir,kıyaslanabilir olduğunu belirtmiş oluyoruz.

func equ<T where T:Equatable>(x:T,_ y:T) -> Bool {
    
    return x == y ? true : false
}

equ(414, "414") // false
equ(3.1, 3.1) // true

Bu örneğimde ise Eşitliği kontrol edilebilir yani Equatable olduğunu bildirdik.

Bu yazdığımız ekler birer protokoldürler. Bizde kendi protokollerimizi yazıp deneyelim.

protocol Sumable {
    func + (x:Self,y:Self) -> Self
}


extension Int:Sumable {
    
    func plus<T:Sumable>(x:T,_ y:T) -> T {
        return x + y
    }
    
}

Int().plus(1,9) // 10

Yazdığım bu örnekte protokol için overloading yapılmış + fonksiyonunu Int sınıfına extend edip generics olarak kullanıyoruz.

Aşağıdaki vereceğim örnekte ise tüm bunların hepsini bir arada kullanacağız. Yine bir veri yapısı türü olan Stack yapısını generics ile gerçekleyeceğim. Kuyruk yapısından farkı LİFO(Last in first out) yapısında olması. Programlamada karşımıza sık çıkmakta İOS sayfalar arası geçişlerde ViewController’leri stack yapısında oluşturur ve geri gittiğinizde pop ile stack’den sayfa çekilmiş olur.

protocol Container {

    associatedtype Item
    mutating func push(item:Item)
    mutating func pop()
    var count:Int {get}
    subscript(i:Int) -> Item {get}
    
}

Container adlı bir protokolümüz var Stack yapısına uygun bir şekilde. Generics işlemimiz için kullanacağız.

struct Stack<Element>:Container {
    
    var items = [Element]()
    
    mutating func push(item:Element) {
        items.append(item)
    }
    
    mutating func pop(){
        items.removeLast()
    }
    
    var count:Int {
        return items.count
    }
    
    subscript(i:Int) -> Element {
        return items[i]
    }
}

Stack structumuzu yazdık aşağıda 2 tane stack oluşturalım.

var stack = Stack<String>()
stack.push("A")
stack.push("B")
stack.push("C")
stack.items // A,B,C
stack.pop() // A,B

var stack2 = Stack<String>()
stack2.push("C")
stack2.push("A")
stack2.push("B")
stack2.items // C,A,B
stack2.pop() // B

Generics üzerinden Where Clauses mantığını anlamak için Generics bir karşılaştırma fonksiyonu yazalım bu stackler aynı mı değil mi bize Bool değer döndürsün.

func itemsMatch<c1:Container,c2:Container where c1.Item == c2.Item, c1.Item:Equatable , c2.Item:Equatable>(item1:c1,_ item2:c2) -> Bool {
    
    if item1.count != item2.count {
        return false
    }
    
    for i in 0..<item1.count {
       
        if item1[i] != item2[i] {
            return false
        }
    }
    
    return true
}

itemsMatch(stack, stack2) // false

c1 ve c2 tipleri burda aynı protokolu kullanan tipler eğer bunların type’leri yanı String veya Int gibi. eşit ise karşılaştırılabilir olduğunu belirtiyoruz. 🙂

 

 

Kategori:Swift

Bu yazı yorumlara kapalı.

Copyright © 2022 Kenan Atmaca