Skip to content

Instantly share code, notes, and snippets.

@KentarouKanno
Last active May 2, 2018 22:00
Show Gist options
  • Save KentarouKanno/dcc8884d8ba1a4dabcbd9cf904fa352e to your computer and use it in GitHub Desktop.
Save KentarouKanno/dcc8884d8ba1a4dabcbd9cf904fa352e to your computer and use it in GitHub Desktop.

Generics2

参考URL: https://qiita.com/shoheiyokoyama/items/31eca0d4b27bc9608eb8

/* Generics */

/* Generics - Generic Types */

// struct
struct S<T> {}
let s  = S<Int>()    /* S<Int> */
let ss = S<String>() /* S<String> */

// class
class C<T> {}
let c  = C<Int>()    /* C<Int> */
let cc = C<String>() /* C<String> */

// enum
enum E<T> {
    case e(T)
}
let e  = E.e(1)   /* E<Int> */
let ee = E.e("a") /* E<String> */

struct Queue<Element> {
    private var elements: [Element] = []
    mutating func enqueue(_ newElement: Element) {
        elements.append(newElement)
    }
    mutating func dequeue() -> Element? {
        guard !elements.isEmpty else { return nil }
        return elements.removeFirst()
    }
}

var intQueue = Queue<Int>()
intQueue.enqueue(3) /* Queue<Int>(elements: [3]) */
intQueue.enqueue(5) /* Queue<Int>(elements: [3, 5]) */
intQueue.dequeue() /* 3 */
intQueue.dequeue() /* 5 */
intQueue.dequeue() /* nil */

var stringQueue = Queue<String>()
stringQueue.enqueue("Generics") /* Queue<String>(elements: ["Generics"]) */
stringQueue.enqueue("Generic Types") /* Queue<String>(elements: ["Generics", "Generic Types"]) */

// extension
extension Queue {
    func peek() -> Element? {
        return elements.first
    }
}

stringQueue.peek() /* "Generics" */


/* --- Generics - 継承 --- */

class BaseClass_1<T> {}

// 継承したクラスもGenerics
class InheritClass<T>: BaseClass_1<T> {}

// 親のクラスをString型にしてそれを継承
class StringClass: BaseClass_1<String> {}


// Protocol - Associated Types
protocol Container {
    associatedtype ItemType
    
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

// Protocol継承 - Non-generic type
struct IntStack: Container {
    typealias ItemType = Int
    
    var items: [Int] = []
    mutating func append(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

var intStack = IntStack()
intStack.append(item: 2)
intStack.append(item: 3)
intStack.append(item: 4)

intStack.count
let result = intStack[2]

/* Generics Types - Protocol Associates Types */
struct Queue1<Element>: Container {
    
    private var elements: [Element] = []
    
    mutating func enqueue(_ newElement: Element) {
        elements.append(newElement)
    }
    mutating func dequeue() -> Element? {
        guard !elements.isEmpty else { return nil }
        return elements.removeFirst()
    }
    mutating func append(item: Element) {
        enqueue(item)
    }
    var count: Int {
        return elements.count
    }
    subscript(i: Int) -> Element {
        return elements[i]
    }
}

var intQueue1 = Queue1<Int>()
intQueue1.append(item: 3)
intQueue1.append(item: 5)
intQueue1[1]
intQueue1.count

var stringQueue1 = Queue1<String>()
stringQueue1.append(item: "Generics")
stringQueue1.append(item: "Generic Types")
stringQueue1[0]
stringQueue1.count

/* Generic Functions */

// Non Generic Function
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var intValue1 = 2
var intValue2 = 5
swapTwoInts(&intValue1, &intValue2)
intValue1 /* 5 */
intValue2 /* 2 */

// Generic Function
func swapToValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var stringValue1 = "a"
var stringValue2 = "b"
swapToValues(&stringValue1, &stringValue2)
stringValue1 /* b */
stringValue2 /* a */

var doubleValue1 = 0.12
var doubleValue2 = 3.45
swapToValues(&doubleValue1, &doubleValue2)
doubleValue1 /* 3.45 */
doubleValue2 /* 0.12 */


/* Type Constraints */

protocol MyProtocol {}
class MyClass {}

// MyClassとMyProtocolの引数
func myFunction<T: MyClass, U: MyProtocol>(someT: T, someU: U) {
    // function body goes here
}

class BaseClass {}
class MyClass1: BaseClass {}             /* BseClass Only */
class MyClass2: BaseClass, MyProtocol {} /* BseClass & MyProtocol */
class MyClass3: MyProtocol {}            /* MyProtocol Only */

// where条件付き Tは BaseClass & MyProtocol
func myFunction<T>(value: T) where T: BaseClass, T: MyProtocol {
    // function body goes here
}

let myClass1 = MyClass1()
// myFunction(value: myClass1) /* Error */

let myClass2 = MyClass2()
myFunction(value: myClass2)

let myClass3 = MyClass3()
// myFunction(value: myClass3) /* Error */


// TがComparableに準拠していること
func findLargestInArray<T: Comparable>(array: [T]) -> T? {
    guard var largestValue = array.first else { return nil }
    
    array.forEach { val in
        largestValue = val > largestValue ? val : largestValue
    }
    return largestValue
}

let intArray = [1, 2, 3, 4, 5]
let largestValue1 = findLargestInArray(array: intArray)
//=> 5

let stringArray = ["a", "b", "c", "d", "e"]
let largestValue2 = findLargestInArray(array: stringArray)
//=> "e"


/* Where Clauses */

// Containerプロとコトルに準拠する配列Itemが、すべての同じかどうかを判定するメソッド true: 同じ, false: 違う
func allItemsMatch<C1: Container, C2: Container>(someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
    if someContainer.count != anotherContainer.count {
        return false
    }
    for i in 0..<someContainer.count {
        if someContainer[i] != anotherContainer[i] {
            return false
        }
    }
    return true
}

// 既存のProtocolの要素に条件をつけてメソッドを定義
extension Collection where Element: Comparable {
    func largestValue() -> Element? {
        guard var largestValue = first else { return nil }
        
        for item in self {
            if item > largestValue {
                largestValue = item
            }
        }
        return largestValue
    }
}

let largestInt = [1, 2, 3, 4, 5].largestValue()  /* 5 */
let largestCharacter = "Generics".largestValue() /* "s" */
let largestValue = (0...100).largestValue()      /* 100 */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment