web-dev-qa-db-ja.com

Swiftで型付き配列を拡張するにはどうすればよいですか?

SwiftのArray<T>またはT[]型をカスタム機能ユーティリティで拡張するにはどうすればよいですか?

SwiftのAPIドキュメントを参照すると、ArrayメソッドがT[]の拡張であることがわかります。例:

extension T[] : ArrayType {
    //...
    init()

    var count: Int { get }

    var capacity: Int { get }

    var isEmpty: Bool { get }

    func copy() -> T[]
}

同じソースをコピーして貼り付け、次のようなバリエーションを試す場合:

extension T[] : ArrayType {
    func foo(){}
}

extension T[] {
    func foo(){}
}

次のエラーでビルドに失敗します。

名義型T[]は拡張できません

完全な型定義の使用は、Use of undefined type 'T'で失敗します。つまり:

extension Array<T> {
    func foo(){}
}

また、Array<T : Any>およびArray<String>でも失敗します。

奇妙なことに、Swiftを使用すると、次のように型なし配列を拡張できます。

extension Array {
    func each(fn: (Any) -> ()) {
        for i in self {
            fn(i)
        }
    }
}

どちらで呼び出すことができます:

[1,2,3].each(println)

しかし、型がメソッドを通過すると失われるように見えるため、適切な汎用型拡張を作成することはできません、例えば Swiftの組み込みフィルターを置き換えよう

extension Array {
    func find<T>(fn: (T) -> Bool) -> T[] {
        var to = T[]()
        for x in self {
            let t = x as T
            if fn(t) {
                to += t
            }
        }
        return to
    }
}

ただし、コンパイラは、次のように拡張機能を呼び出すことができる場合、型指定なしとして扱います。

["A","B","C"].find { $0 > "A" }

そして、デバッガーのステップスルーがタイプがSwift.Stringであることを示しているが、最初にStringにキャストせずにStringのようにアクセスしようとすると、ビルドエラーになります。

["A","B","C"].find { ($0 as String).compare("A") > 0 }

組み込みの拡張機能のように動作する型指定された拡張機能メソッドを作成する適切な方法は誰か知っていますか?

180
mythz

型付き配列をclassesで拡張する場合、以下が機能します(Swift 2.2)。たとえば、型付き配列のソート:

class HighScoreEntry {
    let score:Int
}

extension Array where Element:HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { $0.score < $1.score }
    }
}

structまたはtypealiasでこれを行おうとするとエラーになります:

Type 'Element' constrained to a non-protocol type 'HighScoreEntry'

更新

型付き配列をnon-classesで拡張するには、次のアプローチを使用します。

typealias HighScoreEntry = (Int)

extension SequenceType where Generator.Element == HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { $0 < $1 }
    }
}

Swiftでは、いくつかのタイプの名前が変更されています:

extension Sequence where Iterator.Element == HighScoreEntry 
{
    // ...
}
242

しばらくして別のことを試してみたところ、解決策は署名から<T>を削除するようです:

extension Array {
    func find(fn: (T) -> Bool) -> [T] {
        var to = [T]()
        for x in self {
            let t = x as T;
            if fn(t) {
                to += t
            }
        }
        return to
    }
}

ビルドエラーなしで意図したとおりに動作するようになりました。

["A","B","C"].find { $0.compare("A") > 0 }
62
mythz

同様の問題がありました-配列と同じ型の引数を取ると想定されていたswap()メソッドで一般的な配列を拡張したかったのです。しかし、ジェネリック型をどのように指定しますか?私は試行錯誤によって、以下が機能することを発見しました:

extension Array {
    mutating func swap(x:[Element]) {
        self.removeAll()
        self.appendContentsOf(x)
    }
}

その鍵は「要素」という言葉でした。この型はどこにも定義しておらず、配列拡張のコンテキスト内に自動的に存在し、配列の要素の型が何であれ参照していることに注意してください。

私はそこで何が起こっているのか100%はわかりませんが、おそらく「要素」が配列の関連型であるためだと思います(ここの「関連型」を参照してください https://developer.Apple.com/library /ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//Apple_ref/doc/uid/TP40014097-CH26-ID189

ただし、Array構造の参照( https://developer.Apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//Apple_ref/Swift/struct/s:Sa )...だから私はまだ少し確信が持てない。

8
Daniel Howard

このgithubリポジトリの配列およびクラスのビルドチェックアウトコードの拡張について学習したい場合 https://github.com/ankurp/Cent

Xcode 6.1現在、配列を拡張する構文は次のとおりです。

extension Array {
    func at(indexes: Int...) -> [Element] {
        ... // You code goes herer
    }
}
5
Encore PTL

Swift 2.2を使用:文字列の配列から重複を削除しようとすると、同様の問題が発生しました。 Arrayクラスに拡張機能を追加することができました。拡張機能を使用すると、目的の機能を実行できます。

extension Array where Element: Hashable {
    /**
     * Remove duplicate elements from an array
     *
     * - returns: A new array without duplicates
     */
    func removeDuplicates() -> [Element] {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        return result
    }

    /**
     * Remove duplicate elements from an array
     */
    mutating func removeDuplicatesInPlace() {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        self = result
    }
}

これらの2つのメソッドをArrayクラスに追加すると、配列の2つのメソッドのいずれかを呼び出して、重複を正常に削除できます。配列の要素はHashableプロトコルに準拠する必要があることに注意してください。今、私はこれを行うことができます:

 var dupes = ["one", "two", "two", "three"]
 let deDuped = dupes.removeDuplicates()
 dupes.removeDuplicatesInPlace()
 // result: ["one", "two", "three"]
5
James

拡張allタイプ:

extension Array where Element: Comparable {
    // ...
}

拡張someタイプ:

extension Array where Element: Comparable & Hashable {
    // ...
}

特定タイプを拡張します:

extension Array where Element == Int {
    // ...
}
3
Dmitry

Swift 2の標準ライブラリヘッダーを見ましたが、ここにフィルター関数のプロトタイプがあります。

extension CollectionType {
    func filter(@noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
}

これはArrayの拡張ではなく、CollectionTypeの拡張なので、同じメソッドが他のコレクションタイプに適用されます。 @noescapeは、渡されたブロックがフィルター関数のスコープを離れないことを意味し、最適化を可能にします。大文字のSを持つ自己は、拡張するクラスです。 Self.Generatorはコレクション内のオブジェクトを反復処理する反復子であり、Self.Generator.Elementはオブジェクトの型です。たとえば、配列[Int?] Self.Generator.ElementはInt?になります。

このフィルターメソッドはすべて、すべてのCollectionTypeに適用できます。コレクションの要素を取得してBoolを返すフィルターブロックが必要で、元の型の配列を返します。だから、これをまとめると、私が便利だと思う方法があります:コレクション要素をオプション値にマップするブロックを取ることで、マップとフィルターを組み合わせ、nilではないオプション値の配列を返します。

extension CollectionType {

    func mapfilter<T>(@noescape transform: (Self.Generator.Element) -> T?) -> [T] {
        var result: [T] = []
        for x in self {
            if let t = transform (x) {
                result.append (t)
            }
        }
        return result
    }
}
3
gnasher729
import Foundation

extension Array {
    var randomItem: Element? {
        let idx = Int(arc4random_uniform(UInt32(self.count)))
        return self.isEmpty ? nil : self[idx]
    }
}
2
Leszek Zarna

Swift 2.x

また、ジェネリック型メソッドのblue-rpintsを含むプロトコル、たとえば、プロトコルMyTypesなど、何らかの型制約に準拠するすべてのジェネリック配列要素のカスタム機能ユーティリティを含むプロトコルに準拠するように配列を拡張することもできます。このアプローチを使用することの利点は、これらの配列引数がカスタム関数ユーティリティプロトコル、たとえばprotocol MyFunctionalUtilsに準拠する必要があるという制約付きで、汎用配列引数を取る関数を作成できることです。

この動作は、配列要素をMyTypesに型制約することによって暗黙的に取得できます。または、以下で説明するメソッドで示すように、非常にきれいに、明示的に、汎用配列関数ヘッダーにその入力を直接表示させます配列はMyFunctionalUtilsに準拠しています。


型の制約として使用するためのプロトコルMyTypesから始めます。このプロトコルによってジェネリックに適合させたい型を拡張します(以下の例は、基本型IntおよびDoubleおよびカスタム型MyCustomTypeを拡張します)

/* Used as type constraint for Generator.Element */
protocol MyTypes {
    var intValue: Int { get }
    init(_ value: Int)
    func *(lhs: Self, rhs: Self) -> Self
    func +=(inout lhs: Self, rhs: Self)
}

extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
    // ...

/* Custom type conforming to MyTypes type constraint */
struct MyCustomType : MyTypes {
    var myInt : Int? = 0
    var intValue: Int {
        return myInt ?? 0
    }

    init(_ value: Int) {
        myInt = value
    }
}

func *(lhs: MyCustomType, rhs: MyCustomType) -> MyCustomType {
    return MyCustomType(lhs.intValue * rhs.intValue)
}

func +=(inout lhs: MyCustomType, rhs: MyCustomType) {
    lhs.myInt = (lhs.myInt ?? 0) + (rhs.myInt ?? 0)
}

プロトコルMyFunctionalUtils(追加の汎用配列関数ユーティリティのブループリントを保持)とその後のMyFunctionalUtilsによる配列の拡張。ブループリントされたメソッドの実装:

/* Protocol holding our function utilities, to be used as extension 
   o Array: blueprints for utility methods where Generator.Element 
   is constrained to MyTypes */
protocol MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int?
        // ...
}

/* Extend array by protocol MyFunctionalUtils and implement blue-prints 
   therein for conformance */
extension Array : MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

最後に、一般的な配列を取る関数を示すテストと2つの例、それぞれ次の場合

  1. implicit配列要素を「MyTypes」(関数bar1)に制約する型を介して、配列パラメーターがプロトコル「MyFunctionalUtils」に準拠しているという主張を表示しています。

  2. 配列パラメーターがプロトコル「MyFunctionalUtils」(関数bar2)に準拠していることをexplicitlyに表示しています。

テストと例は次のとおりです。

/* Tests & examples */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1my : [MyCustomType] = [MyCustomType(1), MyCustomType(2), MyCustomType(3)]
let arr2my : [MyCustomType] = [MyCustomType(-3), MyCustomType(-2), MyCustomType(1)]

    /* constrain array elements to MyTypes, hence _implicitly_ constraining
       array parameters to protocol MyFunctionalUtils. However, this
       conformance is not apparent just by looking at the function signature... */
func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK
let myInt1my = bar1(arr1my, arr2my) // -4, OK

    /* constrain the array itself to protocol MyFunctionalUtils; here, we
       see directly in the function signature that conformance to
       MyFunctionalUtils is given for valid array parameters */
func bar2<T: MyTypes, U: protocol<MyFunctionalUtils, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {

    // OK, type U behaves as array type with elements T (=MyTypes)
    var a = arr1
    var b = arr2
    a.append(T(2)) // add 2*7 to multsum
    b.append(T(7))

    return a.foo(Array(b))
        /* Ok! */
}
let myInt2d = bar2(arr1d, arr2d) // 10, OK
let myInt2my = bar2(arr1my, arr2my) // 10, OK
0
dfri