我可以很好地创建各种谓词,但我不确定我可以使用什么来实现“ENDSWITH”?
extension Filter {
var predicate: Predicate<WordModel>? {
switch self {
case .all:
.none
case .lettersContaining(let string):
#Predicate<WordModel> {
$0.letters.localizedStandardContains(string)
}
case .lettersStartingWith(let string):
#Predicate<WordModel> {
$0.letters.starts(with: string)
}
case .lettersEndingWith(let string):
#Predicate<WordModel> {
$0.letters.hasSuffix(string) // 🛑
}
}
}
}
此代码表示“此谓词不支持 hasSuffix(_:) 函数”。
我可以在文档中看到(在PredicateExpressions)谓词闭包是一个构建器表达式,可以包含一组固定的表达式,我只是不确定在我的用例中使用什么。
您使用什么来为 SwiftData 创建“ENDSWITH”谓词
@Query
?
在撰写本文时,这绝对不存在。您可以在
#Predicate
宏支持的所有功能列表这里。
private var _knownSupportedFunctions: Set<FunctionStructure> = [
FunctionStructure("contains", arguments: [.unlabeled]),
FunctionStructure("contains", arguments: [.closure(labeled: "where")]),
FunctionStructure("allSatisfy", arguments: [.closure(labeled: nil)]),
FunctionStructure("flatMap", arguments: [.closure(labeled: nil)]),
FunctionStructure("filter", arguments: [.closure(labeled: nil)]),
FunctionStructure("subscript", arguments: [.unlabeled]),
FunctionStructure("subscript", arguments: [.unlabeled, "default"]),
FunctionStructure("starts", arguments: ["with"]),
FunctionStructure("min", arguments: []),
FunctionStructure("max", arguments: []),
FunctionStructure("localizedStandardContains", arguments: [.unlabeled]),
FunctionStructure("localizedCompare", arguments: [.unlabeled]),
FunctionStructure("caseInsensitiveCompare", arguments: [.unlabeled])
]
可以实现此行为,但您将无法使用
#Predicate
宏来使用它,而且它不会很漂亮。
ends(with:)
函数令我惊讶的是,没有
ends(with:)
与 starts(with:)
对应。
我们可以实现一个:
extension BidirectionalCollection where Element: Equatable {
// Adapted from https://github.com/apple/swift/blob/d6f940/stdlib/public/core/SequenceAlgorithms.swift#L281-L286
@inlinable
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix
) -> Bool where PossibleSuffix.Element == Element {
return self.ends(with: possibleSuffix, by: ==)
}
// Adapted from https://github.com/apple/swift/blob/d6f940/stdlib/public/core/SequenceAlgorithms.swift#L235-L252
@inlinable
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix,
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
) rethrows -> Bool {
var possibleSuffixIterator = possibleSuffix.reversed().makeIterator()
for e0 in self.reversed() {
if let e1 = possibleSuffixIterator.next() {
if try !areEquivalent(e0, e1) {
return false
}
}
else {
return true
}
}
return possibleSuffixIterator.next() == nil
}
}
PredicateExpression
接下来,我们需要添加包装它所需的各种谓词类型。我通过复制
SequenceStartsWith
相关代码并根据需要进行调整来编写所有这些。
extension PredicateExpressions {
// Adapted from `SequenceStartsWith`
// https://github.com/apple/swift-foundation/blob/c4fa86/Sources/FoundationEssentials/Predicate/Expressions/Sequence.swift#L101
public struct CollectionEndsWith<
Base : PredicateExpression,
Suffix : PredicateExpression
> : PredicateExpression
where
Base.Output : BidirectionalCollection,
Suffix.Output : BidirectionalCollection,
Base.Output.Element == Suffix.Output.Element,
Base.Output.Element : Equatable
{
public typealias Output = Bool
public let base: Base
public let suffix: Suffix
public init(base: Base, suffix: Suffix) {
self.base = base
self.suffix = suffix
}
public func evaluate(_ bindings: PredicateBindings) throws -> Bool {
try base.evaluate(bindings).ends(with: try suffix.evaluate(bindings))
}
}
}
// Adapted from https://github.com/apple/swift-foundation/blob/c4fa86/Sources/FoundationEssentials/Predicate/Expressions/Sequence.swift#L226-L239
@available(FoundationPredicate 0.1, *)
extension PredicateExpressions.CollectionEndsWith : Codable where Base : Codable, Suffix : Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(base)
try container.encode(suffix)
}
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.base = try container.decode(Base.self)
self.suffix = try container.decode(Suffix.self)
}
}
// Adapted from https://github.com/apple/swift-foundation/blob/c4fa86/Sources/FoundationEssentials/Predicate/Expressions/Sequence.swift#L174-L175
@available(FoundationPredicate 0.1, *)
extension PredicateExpressions.CollectionEndsWith : StandardPredicateExpression where Base : StandardPredicateExpression, Suffix : StandardPredicateExpression {}
extension PredicateExpressions {
// Adapted from `build_starts`
// https://github.com/apple/swift-foundation/blob/c4fa86/Sources/FoundationEssentials/Predicate/Expressions/Sequence.swift#L139
public static func build_ends<Base, Suffix>(_ base: Base, with suffix: Suffix) -> CollectionEndsWith<Base, Suffix> {
CollectionEndsWith(base: base, suffix: suffix)
}
}
最后,我们需要实际使用这个新谓词。不幸的是,我们没有办法修改/扩展
#Predicate
宏语法,除非分叉它。
我们可以做的是,使用
#Predicate
和 starts(with:)
调用来启动,例如
let people = [
"Alice",
"Alex",
"Bob",
"Charlie",
"Daniel",
].map(Person.init)
let predicate = #Predicate<Person> { person in
person.name.starts(with: "A")
}
然后我们可以使用
swift -Xfrontend -dump-macro-expansions predicate_demo.swift
转储该宏的扩展:
Macro expands to:
let predicate = Foundation.Predicate<Person>({ person in
PredicateExpressions.build_starts(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(person),
keyPath: \.name
),
with: PredicateExpressions.build_Arg("A")
)
})
然后我们可以将
build_starts
修改为 build_ends
,以获得最终的示例:
let predicate = Foundation.Predicate<Person>({ person in
PredicateExpressions.build_ends( // Replaced `.build_starts(`
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(person),
keyPath: \.name
),
with: PredicateExpressions.build_Arg("e")
)
})
let namesEndingWithE = try people.filter(predicate).map(\.name)
print(namesEndingWithE) // ["Alice", "Charlie"]