bản đồ (keyPath) trong đó keyPath là một biến
let arr = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
arr.map(\.0) // [1, 2, 3, 4, 5]
Hoạt động tuyệt vời. Nhưng đoạn mã dưới đây không biên dịch:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Không thể chuyển đổi giá trị kiểu 'WordsKeyPath <(Int, Int), Int>' thành kiểu đối số mong đợi '((Int, Int)) ném -> T'.
Không thể suy ra tham số chung 'T'.
Trả lời
Array.mapmong đợi một sự đóng cửa với chữ ký (Element) throws -> T.
Trong Swift 5.2, các đường dẫn chính được phép chuyển vào dưới dạng hàm / đóng (đây là một đề xuất tiến hóa ), nhưng chỉ dưới dạng chữ (ít nhất, theo đề xuất, nó nói "bây giờ", vì vậy có lẽ hạn chế này sẽ được dỡ bỏ ).
Để khắc phục điều này, bạn có thể tạo một tiện ích mở rộng trên Sequenceđó chấp nhận một đường dẫn chính:
extension Sequence {
func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
return map { $0[keyPath: keyPath] }
}
}
(tín dụng để: https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/)
Sau đó, bạn có thể làm những gì bạn muốn:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Đề xuất phát triển đã chỉ ra cách thực hiện điều đó với một toán tử, nhưng bạn cũng có thể sử dụng cùng một []hoặc ()cú pháp cho dù nó được áp dụng một phần hay không, bởi vì các chỉ số và hàm không cần đối số.
let oneTo5 = 1...5
let keyPath = \(Int, Int).0
XCTAssert(
zip(oneTo5, oneTo5).map(keyPath[]).elementsEqual(oneTo5)
)
let keyPath = \Double.isZero
XCTAssertFalse(keyPath[1.0]())
public extension KeyPath {
/// Convert a `KeyPath` to a partially-applied get accessor.
subscript() -> (Root) -> Value {
{ $0[keyPath: self] } } /// Convert a `KeyPath` to a get accessor. subscript(root: Root) -> () -> Value { { root[keyPath: self] } } } public extension ReferenceWritableKeyPath { /// Convert a `KeyPath` to a partially-applied get/set accessor pair. subscript() -> (Root) -> Computed<Value> { { self[$0] }
}
/// Convert a `KeyPath` to a get/set accessor pair.
subscript(root: Root) -> Computed<Value> {
.init(
get: self[root],
set: { root[keyPath: self] = $0 }
)
}
}
/// A workaround for limitations of Swift's computed properties.
///
/// Limitations of Swift's computed property accessors:
/// 1. They are not mutable.
/// 2. They cannot be referenced as closures.
@propertyWrapper public struct Computed<Value> {
public typealias Get = () -> Value
public typealias Set = (Value) -> Void
public init(
get: @escaping Get,
set: @escaping Set
) {
self.get = get
self.set = set
}
public var get: Get
public var set: Set
public var wrappedValue: Value {
get { get() }
set { set(newValue) }
}
public var projectedValue: Self {
get { self }
set { self = newValue }
}
}
//MARK:- public
public extension Computed {
init(
wrappedValue: Value,
get: @escaping Get = {
fatalError("`get` must be assigned before accessing `wrappedValue`.")
},
set: @escaping Set
) {
self.init(get: get, set: set)
self.wrappedValue = wrappedValue
}
}