gpt4 book ai didi

swift - 如何使用 Swift KeyPaths 的异构数组

转载 作者:搜寻专家 更新时间:2023-10-30 22:11:00 25 4
gpt4 key购买 nike

似乎应该可以使用 KeyPath 数组作为排序键,使用任意数量的排序键对 Swift 结构数组进行排序。 从概念上讲,这很简单。您定义一个通用对象的 KeyPaths 数组,其中唯一的限制是键路径中的属性是 Comparable

只要所有的 KeyPaths 指向相同类型的属性,一切都很好。但是,一旦您尝试使用指向数组中不同类型元素的 KeyPaths,它就会停止工作。

请参阅下面的代码。我创建了一个具有 2 个 Int 属性和一个 double 属性的简单结构。我创建了一个 Array 扩展,它实现了一个函数 sortedByKeypaths(_:) 该函数指定了一个通用类型 PROPERTY,它是 Comparable。它采用指向某个对象元素的 kepaths 数组,该元素指定 PROPERTY 类型的属性。 (可比较的属性。)

只要您使用 KeyPaths 数组调用该函数到所有相同类型的属性,它就可以完美运行。

但是,如果您尝试将键路径数组传递给不同类型的属性,则会引发错误 “无法将‘[PartialKeyPath]’类型的值转换为预期的参数类型‘[KeyPath]’”

由于数组包含异构键路径,由于类型删除,数组将转变成类型“[PartialKeyPath]”,您不能使用 PartialKeyPath 从数组中获取元素。

这个问题有解决办法吗?无法使用异构的 KeyPath 数组似乎严重限制了 Swift KeyPath 的实用性

import UIKit

struct Stuff {
let value: Int
let value2: Int
let doubleValue: Double
}

extension Array {

func sortedByKeypaths<PROPERTY: Comparable>(_ keypaths: [KeyPath<Element, PROPERTY>]) -> [Element] {
return self.sorted { lhs, rhs in
var keypaths = keypaths
while !keypaths.isEmpty {
let keypath = keypaths.removeFirst()
if lhs[keyPath: keypath] != rhs[keyPath: keypath] {
return lhs[keyPath: keypath] < rhs[keyPath: keypath]
}
}
return true
}
}
}

var stuff = [Stuff]()

for _ in 1...20 {
stuff.append(Stuff(value: Int(arc4random_uniform(5)),
value2: Int(arc4random_uniform(5)),
doubleValue: Double(arc4random_uniform(10))))
}

let sortedStuff = stuff.sortedByKeypaths([\Stuff.value, \Stuff.value2]) //This works
sortedStuff.forEach { print($0) }

let moreSortedStuff = stuff.sortedByKeypaths([\Stuff.value, \Stuff.doubleValue]) //This throws a compiler error
moreSortedStuff.forEach { print($0) }

最佳答案

使用部分键路径数组的问题是您无法保证属性类型是 Comparable .一种可能的解决方案是使用类型删除包装器来删除键路径的值类型,同时确保它是 Comparable。 :

struct PartialComparableKeyPath<Root> {

private let _isEqual: (Root, Root) -> Bool
private let _isLessThan: (Root, Root) -> Bool

init<Value : Comparable>(_ keyPath: KeyPath<Root, Value>) {
self._isEqual = { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
self._isLessThan = { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}

func isEqual(_ lhs: Root, _ rhs: Root) -> Bool {
return _isEqual(lhs, rhs)
}

func isLessThan(_ lhs: Root, _ rhs: Root) -> Bool {
return _isLessThan(lhs, rhs)
}
}

然后您可以将排序功能实现为:

extension Sequence {

func sorted(by keyPaths: PartialComparableKeyPath<Element>...) -> [Element] {
return sorted { lhs, rhs in
for keyPath in keyPaths {
if !keyPath.isEqual(lhs, rhs) {
return keyPath.isLessThan(lhs, rhs)
}
}
return false
}
}
}

然后像这样使用:

struct Stuff {
let value: Int
let value2: Int
let doubleValue: Double
}

var stuff = [Stuff]()

for _ in 1 ... 20 {
stuff.append(Stuff(value: Int(arc4random_uniform(5)),
value2: Int(arc4random_uniform(5)),
doubleValue: Double(arc4random_uniform(10))))
}


let sortedStuff = stuff.sorted(by: PartialComparableKeyPath(\.value),
PartialComparableKeyPath(\.value2))
sortedStuff.forEach { print($0) }

let moreSortedStuff = stuff.sorted(by: PartialComparableKeyPath(\.value),
PartialComparableKeyPath(\.doubleValue))
moreSortedStuff.forEach { print($0) }

尽管不幸的是,这需要将每个单独的键路径包装在 PartialComparableKeyPath 中value 以捕获和删除键路径的值类型,这不是特别漂亮。

我们真正需要的特征是 variadic generics ,这将允许您在可变数量的通用占位符上为您的键路径的值类型定义您的函数,每个占位符都限制为 Comparable .

在那之前,另一种选择是只为不同数量的键路径编写给定数量的重载以进行比较:

extension Sequence {
func sorted<A : Comparable>(by keyPathA: KeyPath<Element, A>) -> [Element] {
return sorted { lhs, rhs in
lhs[keyPath: keyPathA] < rhs[keyPath: keyPathA]
}
}

func sorted<A : Comparable, B : Comparable>
(by keyPathA: KeyPath<Element, A>, _ keyPathB: KeyPath<Element, B>) -> [Element] {
return sorted { lhs, rhs in
(lhs[keyPath: keyPathA], lhs[keyPath: keyPathB]) <
(rhs[keyPath: keyPathA], rhs[keyPath: keyPathB])
}
}

func sorted<A : Comparable, B : Comparable, C : Comparable>
(by keyPathA: KeyPath<Element, A>, _ keyPathB: KeyPath<Element, B>, _ keyPathC: KeyPath<Element, C>) -> [Element] {
return sorted { lhs, rhs in
(lhs[keyPath: keyPathA], lhs[keyPath: keyPathB], lhs[keyPath: keyPathC]) <
(rhs[keyPath: keyPathA], rhs[keyPath: keyPathB], rhs[keyPath: keyPathC])
}
}

func sorted<A : Comparable, B : Comparable, C : Comparable, D : Comparable>
(by keyPathA: KeyPath<Element, A>, _ keyPathB: KeyPath<Element, B>, _ keyPathC: KeyPath<Element, C>, _ keyPathD: KeyPath<Element, D>) -> [Element] {
return sorted { lhs, rhs in
(lhs[keyPath: keyPathA], lhs[keyPath: keyPathB], lhs[keyPath: keyPathC], lhs[keyPath: keyPathD]) <
(rhs[keyPath: keyPathA], rhs[keyPath: keyPathB], rhs[keyPath: keyPathC], rhs[keyPath: keyPathD])
}
}

func sorted<A : Comparable, B : Comparable, C : Comparable, D : Comparable, E : Comparable>
(by keyPathA: KeyPath<Element, A>, _ keyPathB: KeyPath<Element, B>, _ keyPathC: KeyPath<Element, C>, _ keyPathD: KeyPath<Element, D>, _ keyPathE: KeyPath<Element, E>) -> [Element] {
return sorted { lhs, rhs in
(lhs[keyPath: keyPathA], lhs[keyPath: keyPathB], lhs[keyPath: keyPathC], lhs[keyPath: keyPathD], lhs[keyPath: keyPathE]) <
(rhs[keyPath: keyPathA], rhs[keyPath: keyPathB], rhs[keyPath: keyPathC], rhs[keyPath: keyPathD], rhs[keyPath: keyPathE])
}
}

func sorted<A : Comparable, B : Comparable, C : Comparable, D : Comparable, E : Comparable, F : Comparable>
(by keyPathA: KeyPath<Element, A>, _ keyPathB: KeyPath<Element, B>, _ keyPathC: KeyPath<Element, C>, _ keyPathD: KeyPath<Element, D>, _ keyPathE: KeyPath<Element, E>, _ keyPathF: KeyPath<Element, F>) -> [Element] {
return sorted { lhs, rhs in
(lhs[keyPath: keyPathA], lhs[keyPath: keyPathB], lhs[keyPath: keyPathC], lhs[keyPath: keyPathD], lhs[keyPath: keyPathE], lhs[keyPath: keyPathF]) <
(rhs[keyPath: keyPathA], rhs[keyPath: keyPathB], rhs[keyPath: keyPathC], rhs[keyPath: keyPathD], rhs[keyPath: keyPathE], rhs[keyPath: keyPathF])
}
}
}

我已经为它们定义了最多 6 个键路径,这对于大多数排序情况应该足够了。我们正在利用 < 的词典顺序元组比较重载在这里,也展示了here .

虽然实现并不漂亮,但调用站点现在看起来好多了,因为它让您说:

let sortedStuff = stuff.sorted(by: \.value, \.value2)
sortedStuff.forEach { print($0) }

let moreSortedStuff = stuff.sorted(by: \.value, \.doubleValue)
moreSortedStuff.forEach { print($0) }

关于swift - 如何使用 Swift KeyPaths 的异构数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50136303/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com