map (keyPath), burada keyPath bir değişken

Sep 06 2020
let arr = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
arr.map(\.0) // [1, 2, 3, 4, 5]

Harika çalışıyor. Ancak aşağıdaki kod derlenmez:

let keyPath = \(Int, Int).0
arr.map(keyPath)

'WritableKeyPath <(Int, Int), Int>' türünün değeri beklenen bağımsız değişken türüne dönüştürülemiyor '((Int, Int)) -> T' atar.
Genel parametre 'T' çıkarılamadı.

Yanıtlar

4 NewDev Sep 06 2020 at 20:41

Array.mapimzalı bir kapanış bekliyor (Element) throws -> T.

Swift 5.2'de, temel yolların işlevler / kapanışlar olarak aktarılmasına izin verildi (burada bir evrim önerisi var ), ancak yalnızca değişmez değerler olarak (en azından, teklife göre, "şimdilik" diyor, bu nedenle belki bu kısıtlama kaldırılabilirdi) ).

Bunun üstesinden gelmek için, üzerinde Sequencebir anahtar yolu kabul eden bir uzantı oluşturabilirsiniz :

extension Sequence {
   func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
      return map { $0[keyPath: keyPath] }
   }
}

(kredi: https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/)

O zaman istediğini yapabilirsin:

let keyPath = \(Int, Int).0
arr.map(keyPath)
Jessy Sep 06 2020 at 22:24

Evrim önerisi bunun bir operatörle nasıl yapılacağını gösterdi, ancak kısmen uygulanıp uygulanmadığına bakılmaksızın aynı []veya ()sözdizimini de kullanabilirsiniz , çünkü alt simgeler ve işlevler bağımsız değişkenlere ihtiyaç duymaz.

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
  }
}