次のSwift 2.2コードをSwift 3でビルドできるようにする。
//
// SequenceTests.swift
//
import XCTest
class SequenceTests: XCTestCase {
func testExample() {
XCTAssertTrue(contain([1, 2])([1, 2]))
XCTAssertFalse(contain([2, 3])([1, 2]))
}
}
extension SequenceType {
func all(predicate: Generator.Element -> Bool) -> Bool {
return reduce(true) { $0 && predicate($1) }
}
}
func contain<S: SequenceType, T: Equatable where S.Generator.Element == T>(items: [T]) -> S -> Bool {
return { sequence in
sequence.all(items.contains)
}
}
Swift 3での変更
SequenceType
を Sequence
SequenceType.Generator
を Sequence.Iterator
単純に書き直すと
@@ -10,13 +10,13 @@ class SequenceTests: XCTestCase {
}
}
-extension SequenceType {
- func all(predicate: Generator.Element -> Bool) -> Bool {
+extension Sequence {
+ func all(predicate: Iterator.Element -> Bool) -> Bool {
return reduce(true) { $0 && predicate($1) }
}
}
-func contain<S: SequenceType, T: Equatable where S.Generator.Element == T>(items: [T]) -> S -> Bool {
+func contain<S: Sequence, T: Equatable where S.Iterator.Element == T>(items: [T]) -> S -> Bool {
return { sequence in
sequence.all(items.contains)
}
Swift 3 の Sequence
で書いておいて、Swift 2.2の場合 SequenceType
となるように typealiase
を書くと Swift 2.2 と 3 の両方でビルドできるコードになるのでは?
typealias
を書いてみると
#if !swift(>=3)
typealias Sequence = SequenceType
typealias Sequence.Iterator = SequenceType.Generator
#endif
ビルドエラー
SequenceTests.swift:27:23: expected '=' in typealias declaration
typealias Sequence.Iterator = SequenceType.Generator
^
associatedtype
が指定できない。
ならば protocol Sequence
を定義。
#if !swift(>=3)
public protocol Sequence: SequenceType {
associatedtype Iterator: GeneratorType = Generator
}
#endif
ビルドエラー
SequenceTests.swift:15:29: cannot convert value of type '(_, Self.Iterator.Element) -> Bool' to expected argument type '(Bool, _) -> Bool'
return reduce(true) { $0 && predicate($1) }
^
どうやら S.Generator.Element: Equatable
の制約がつかず、元の
extension SequenceType where Generator.Element: Equatable {
func contains(element: Self.Generator.Element) -> Bool
}
ではなく、こちら
extension SequenceType {
func contains(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool
}
になってしまう。
Iterator
と Generator
が同じものとみなすオーバーライドしたメソッドを用意するといける。
#if !swift(>=3)
public protocol Sequence: SequenceType {
associatedtype Iterator: GeneratorType = Generator
}
extension Array: Sequence {}
extension Sequence {
internal func reduce<T>(initial: T, @noescape combine: (T, Iterator.Element) throws -> T) rethrows -> T {
return try reduce(initial) { (prevResult, element: Generator.Element) -> T in
try combine(prevResult, element as! Iterator.Element)
}
}
}
#endif