解释这段代码:使用字符串作为过滤函数

Posted

技术标签:

【中文标题】解释这段代码:使用字符串作为过滤函数【英文标题】:Explain this code: uses a string as filter function 【发布时间】:2021-12-03 21:11:36 【问题描述】:

在我监督的一个项目中查看一些 Swift 代码时,我遇到了这样的情况:

return (realm?.objects(ExerciseDBObject.self).filter("isDeleted == false")) as! Results<ExerciseDBObject>

作为一名 JVM/Python/JS 程序员,让我印象深刻的是 filter("isDeleted == false") 位。假设,这段代码工作正常:它过滤了没有删除的练习,但它是一个字符串。这是如何工作的?

我没有使用过 Swift,当我在谷歌上搜索时,我发现了 String#filter 上的文档,这似乎暗示我通常会将那段代码写成 filter(!$0.isDeleted)

字符串的isDeleted 位指的是对象上的一个道具。 Swift 如何避免将其绑定到也称为 isDeleted 的某个变量(如果存在,它不在此代码块中)?

【问题讨论】:

它是func filter(_ predicateFormat: String, _ args: Any...) -&gt; Results&lt;Element&gt;,在后台应该使用NSPredicate(format:)... 旁注:你不能在 Swift 中写 filter(!$0.isDeleted)。你需要关闭filter(!$0.isDeleted) 如答案中所述,.filter("isDeleted 是 Realm 函数,而不是 Swift 函数。 Realm 由 ObjC 对象支持。简而言之,.filter("isDeletedNSPredicate("isDeleted == false") 相同,重要的是.filter 是一个领域对象函数,在本例中为ExerciseDBObject 类。这就是为什么过滤器“知道”对象属性并且不会将它们与另一个变量混淆的原因。 (它不会将它们绑定到某个变量,因为过滤器位于该 Realm 对象上)。 @LeoDabus 啊,真的。正如他们所说,那是一个错误。现已修复。 【参考方案1】:

您可能正在查看“Realm”的一个功能,我认为它是一种用于处理存储对象的工具,而不是 Swift 的一个功能。

本例中的问题是“什么是领域?.objects(...) 返回?”。我猜我会说它不是字符串...它是具有自定义定义 filter 的其他对象,它知道如何解析 String 并将其用作数据获取操作的查询。

这是方法filter 的多态性。它可以根据被调用实体的类做不同的事情。

【讨论】:

我不会称之为多态性,而是重载(我知道很挑剔:p) 我在原始评论中有重载......但是多态是官方祝福的 OO 术语(tm)所以我改变了它。在“信息隐藏”和“封装”之间做出决定时,我也会遇到类似的问题。 ;-) 一些快速的事情。这就是 Realm,它是一个由 MongoDB (NoSQL) 支持的对象数据库。领域不是多态的,但它们是working on it。 .filter 是一个领域集合对象函数,实际上只是一个 NSPredicate。 filter 函数返回与过滤器匹配的其他领域对象的领域结果(称为集合)。您可以在这里看到realm?.objects(aRealmObject).filter 过滤器正在过滤aRealmObject 类型的Realm 对象,因此它知道这些对象的属性。 @Cristik 争论术语基本上是徒劳的。但它们只是不同的概念,具体取决于您是否将“多态性”视为与继承相关联,或者您是否将该术语视为应用可以发送到许多对象的单个选择器(使用 Objective-C 命名法)。在 C++ 中,多态性通常是根据它如何允许子类覆盖超类的方法来描述的。在 Smalltalk 中,多态性描述了相同的消息如何发送到许多不同的对象并引发不同的行为。所以它并不像你想象的那么清晰。 arguing about terminology is largely fruitless - 它可能适合我们,但它可能会帮助其他读者。 *** 的目标是成为知识的百科全书,任何技术辩论都会有所帮助,当然如果保持在文明的水平:) 你的最后评论为讨论增加了一些很好的论据,我认为这些论点值得添加到答案中。 【参考方案2】:

它正在使用:

// MARK: Filtering
/**
 Returns a `Results` containing all objects matching the given predicate in the collection.
 - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
 */
func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element>

来源:RealmCollectiton

在底层,它使用NSPredicate(format:),它通过简化和避免每次写入NSPredicate(format: ...) 来“隐藏”它,并且也应该使用KeyPath。在Predicate Format String Syntax 处了解更多关于你可以用它做什么的说明。

不是

func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> [Self.Element]

Sequence

这里有一个示例代码来说明/模仿:

class TestClass: NSObject 
    @objc var title: String
    init(title: String) 
        self.title = title
        super.init()
    
    override var description: String 
        "TestClass - title: \(title)"
    


let objects: [TestClass] = [TestClass(title: "Title1"), TestClass(title: "Title2")]

// In a "Swifty way
let searchedText = "Title1"
let filtered1 = objects.filter 
    $0.title == searchedText

print("filtered1: \(filtered1)")

// With a NSPredicate, which is more Objective-C (filtering on NSArray for instance)
// But also there is a translation with Core-Data, and then fetching optimization
let filtered2 = objects.filter 
    NSPredicate(format: "title == 'Title1'").evaluate(with: $0)

print("filtered2: \(filtered2)")
// Same, but with avoiding hard coding strings
let filtered3 = objects.filter 
    NSPredicate(format: "%K == %@", #keyPath(TestClass.title), searchedText).evaluate(with: $0)

print("filtered3: \(filtered3)")

extension Sequence 
    func filter(_ predicateFormat: String, _ args: Any...) -> [Element] 
        let predicate = NSPredicate(format: predicateFormat, argumentArray: args)
        return self.filter( predicate.evaluate(with: $0) )
    


// With an extension similar to what does Realm
let filtered4 = objects.filter("title == 'Title1'")
print("filtered4: \(filtered4)")

// With an extension similar to what does Realm, and less hard coded string (cf filtered3 construction)
let filtered5 = objects.filter("%K == %@", #keyPath(TestClass.title), searchedText)
print("filtered5: \(filtered5)")

有输出:

$>filtered1: [TestClass - title: Title1]
$>filtered2: [TestClass - title: Title1]
$>filtered3: [TestClass - title: Title1]
$>filtered4: [TestClass - title: Title1]
$>filtered5: [TestClass - title: Title1]

【讨论】:

以上是关于解释这段代码:使用字符串作为过滤函数的主要内容,如果未能解决你的问题,请参考以下文章

请帮我详细解释一下这段PHP代码,谢谢

谁能解释为啥这段代码不起作用?

递归函数:有人能解释一下这段代码是如何打印 1 到 5 的吗?

Rust - 创建过滤谓词作为函数

绕过PHP代码执行中的过滤限制详解

在函数中使用字符串数组