Swift 一组具有可选字符串属性的对象,改进了接受字符串参数的函数,并在集合中找到最佳匹配

Swift 一组具有可选字符串属性的对象,改进了接受字符串参数的函数,并在集合中找到最佳匹配,swift,algorithm,pattern-matching,string-matching,Swift,Algorithm,Pattern Matching,String Matching,我们正在尝试优化一些我们一直使用的“加权”匹配算法,并决定参考互联网上的更多想法 我们有一个StructMyStruct,有5个可选属性(在swift中,这仅仅意味着属性可以是nil): 然后我们有一组MyStruct(保证没有两个实例具有相同的确切属性) structArray:Set 我们有一个函数,它接受这个数组以及字典中的1-5个属性,以返回一个最匹配的实例。如果任何属性不匹配,实例将立即退出争用 func findBestMatch(forSet set:Set<MyStruct

我们正在尝试优化一些我们一直使用的“加权”匹配算法,并决定参考互联网上的更多想法

我们有一个Struct
MyStruct
,有5个可选属性(在swift中,这仅仅意味着属性可以是
nil
):

然后我们有一组
MyStruct
(保证没有两个实例具有相同的确切属性)

structArray:Set

我们有一个函数,它接受这个数组以及字典中的1-5个属性,以返回一个最匹配的实例。如果任何属性不匹配,实例将立即退出争用

func findBestMatch(forSet set:Set<MyStruct>, andArgs argDict:[String:String]) -> MyStruct? {
  //Will use this to store any matches, as well as an associated match score
  var bestMatch: MyStruct?
  var topScore = 0
  for element in set {
    var score = 0
    if let p1 = argDict["p1"] {
      if p1 == element.prop1 {
        score += 16 //p1 match has highest weight
      } else {
        continue
      }
    }

    if let p2 = argDict["p2"] {
      if p2 == element.prop2 {
        score += 8 //p2 match has second-highest weight
      } else {
        continue
      }
    }

    //etc for the other 3 properties

    if score > topScore {
      topScore = score
      bestMatch = element 
    }
  }
  return bestMatch
}
func findBestMatch(用于set:set和args argDict:[String:String])->MyStruct?{
//将使用此存储任何匹配项以及关联的匹配分数
var bestMatch:MyStruct?
var topScore=0
对于集合中的元素{
风险值得分=0
如果让p1=argDict[“p1”]{
如果p1==element.prop1{
分数+=16//p1匹配具有最高权重
}否则{
持续
}
}
如果让p2=argDict[“p2”]{
如果p2==element.prop2{
分数+=8//p2匹配具有第二高的权重
}否则{
持续
}
}
//其他3个属性的等
如果得分>topScore{
上核=得分
最佳匹配=元素
}
}
返回最佳匹配
}
示例:

exInstance1
  prop1 = "no good"
  prop2 = nil
  prop3 = "goodbye

exInstance2
  prop1 = "hello"
  prop2 = "noproblem"
  prop3 = "goodbye"

exInstance3
  prop1 = nil
  prop2 = nil
  prop3 = "goodbye"

exampleSet: Set<MyStruct> = [exInstance1, exInstance2, exInstance3]

matchingProperties: [String:String] = {
  "p1": "hello",
  "p3": "goodbye"
}


findBestMatch(forSet: exampleSet, andArgs: matchingProperties)
exInstance1
prop1=“不好”
prop2=零
prop3=“再见
存在2
prop1=“你好”
prop2=“无问题”
prop3=“再见”
存在3
prop1=零
prop2=零
prop3=“再见”
exampleSet:Set=[exInstance1、exInstance2、exInstance3]
matchingProperties:[字符串:字符串]={
“p1”:“你好”,
“p3”:“再见”
}
findBestMatch(例如集合:exampleSet和Args:matchingProperties)
Existance 1在prop3上只有1个匹配项,但由于prop1根本不匹配,Existance不会得到分数

exInstance2在两个属性上都匹配,并且得到20分

exInstance3在一个属性上匹配,并得到4分

选择并返回exInstance2



问题:有更好的方法吗?如果没有,我们有什么方法可以改进此算法吗?

如果您排除for循环之外的字典访问,我只会看到轻微的改进,例如

func findBestMatch(forSet set:Set<MyStruct>, andArgs argDict:[String:String]) -> MyStruct? {
    //Will use this to store any matches, as well as an associated match score
    var bestMatch: MyStruct?
    var topScore = 0
    let p1 = argDict["p1"]
    let p2 = argDict["p2"]  // and so on
    for element in set {
        var score = 0
        if let p1 = p1 {
            if p1 == element.prop1 {
                score += 16 //p1 match has highest weight
            } else {
                continue
            }
        }

        //etc for the other properties

        if score > topScore {
            topScore = score
            bestMatch = element
        }
    }
    return bestMatch
}
func findBestMatch(用于set:set和args argDict:[String:String])->MyStruct{
//将使用此存储任何匹配项以及关联的匹配分数
var bestMatch:MyStruct?
var topScore=0
设p1=argDict[“p1”]
设p2=argDict[“p2”]//以此类推
对于集合中的元素{
风险值得分=0
如果让p1=p1{
如果p1==element.prop1{
分数+=16//p1匹配具有最高权重
}否则{
持续
}
}
//其他物业等
如果得分>topScore{
上核=得分
最佳匹配=元素
}
}
返回最佳匹配
}

如果将for循环之外的字典访问排除在外,我只会看到轻微的改进,例如

func findBestMatch(forSet set:Set<MyStruct>, andArgs argDict:[String:String]) -> MyStruct? {
    //Will use this to store any matches, as well as an associated match score
    var bestMatch: MyStruct?
    var topScore = 0
    let p1 = argDict["p1"]
    let p2 = argDict["p2"]  // and so on
    for element in set {
        var score = 0
        if let p1 = p1 {
            if p1 == element.prop1 {
                score += 16 //p1 match has highest weight
            } else {
                continue
            }
        }

        //etc for the other properties

        if score > topScore {
            topScore = score
            bestMatch = element
        }
    }
    return bestMatch
}
func findBestMatch(用于set:set和args argDict:[String:String])->MyStruct{
//将使用此存储任何匹配项以及关联的匹配分数
var bestMatch:MyStruct?
var topScore=0
设p1=argDict[“p1”]
设p2=argDict[“p2”]//以此类推
对于集合中的元素{
风险值得分=0
如果让p1=p1{
如果p1==element.prop1{
分数+=16//p1匹配具有最高权重
}否则{
持续
}
}
//其他物业等
如果得分>topScore{
上核=得分
最佳匹配=元素
}
}
返回最佳匹配
}

在这种情况下,可以进行以下优化:

  • 不要使用字典,而是直接传递参数
  • 在循环的每次迭代中,对每个属性使用可选绑定。这是可以避免的
  • 如果对象的所有字段都不是可选的,并且与传递给函数的字段相同,那么它是最好的
  • 基于上述情况,我提出以下解决方案:

    import Foundation
    
    struct MyStruct {
        var prop1: String? = nil
        var prop2: String? = nil
        var prop3: String? = nil
    }
    
    extension MyStruct: Hashable {}
    
    func findBestMatch(for set: Set<MyStruct>,
                       oProperty1: String? = nil,
                       oProperty2: String? = nil,
                       oProperty3: String? = nil) -> MyStruct?
    {
        let mask000 = 0b00000000
        let mask001 = 0b00000001
        let mask010 = 0b00000010
        let mask011 = 0b00000011
        let mask100 = 0b00000100
        let mask101 = 0b00000101
        let mask110 = 0b00000110
        let mask111 = 0b00000111
    
        var mask = mask000
    
        if let _ = oProperty1 {
            mask |= mask001
        }
        if let _ = oProperty2 {
            mask |= mask010
        }
        if let _ = oProperty3 {
            mask |= mask100
        }
    
        if mask == mask000 {
            return nil
        } else if mask == mask001 {
            let prop3 = oProperty3!
            return set.first(where: { $0.prop3 == prop3 })
        } else if mask == mask010 {
            let prop2 = oProperty2!
            return set.first(where: { $0.prop2 == prop2 })
        } else if mask == mask011 {
            let prop2 = oProperty2!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop2 == prop2 && $0.prop3 == prop3 })
        } else if mask == mask100 {
            let prop1 = oProperty1!
            return set.first(where: { $0.prop1 == prop1 })
        } else if mask == mask101 {
            let prop1 = oProperty1!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop1 == prop1 && $0.prop3 == prop3 })
        } else if mask == mask110 {
            let prop1 = oProperty1!
            let prop2 = oProperty2!
            return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 })
        } else if mask == mask111 {
            let prop1 = oProperty1!
            let prop2 = oProperty2!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 && $0.prop3 == prop3 })
        }
        return nil
    }
    
    let exInstance1 = MyStruct(prop1: "no good", prop2: nil, prop3: "goodbye")
    let exInstance2 = MyStruct(prop1: "hello", prop2: "noproblem", prop3: "goodbye")
    let exInstance3 = MyStruct(prop1: nil, prop2: nil, prop3: "goodbye")
    
    let exampleSet: Set<MyStruct> = [
        exInstance1,
        exInstance2,
        exInstance3,
    ]
    
    if let object = findBestMatch(for: exampleSet, oProperty1: "hello", oProperty2: nil, oProperty3: "goodbye") {
        print(object) // print MyStruct(prop1: Optional("hello"), prop2: Optional("noproblem"), prop3: Optional("goodbye"))
    } else {
        print("not found")
    }
    

    在这种情况下,可以进行以下优化:

  • 不要使用字典,而是直接传递参数
  • 在循环的每次迭代中,对每个属性使用可选绑定。这是可以避免的
  • 如果对象的所有字段都不是可选的,并且与传递给函数的字段相同,那么它是最好的
  • 基于上述情况,我提出以下解决方案:

    import Foundation
    
    struct MyStruct {
        var prop1: String? = nil
        var prop2: String? = nil
        var prop3: String? = nil
    }
    
    extension MyStruct: Hashable {}
    
    func findBestMatch(for set: Set<MyStruct>,
                       oProperty1: String? = nil,
                       oProperty2: String? = nil,
                       oProperty3: String? = nil) -> MyStruct?
    {
        let mask000 = 0b00000000
        let mask001 = 0b00000001
        let mask010 = 0b00000010
        let mask011 = 0b00000011
        let mask100 = 0b00000100
        let mask101 = 0b00000101
        let mask110 = 0b00000110
        let mask111 = 0b00000111
    
        var mask = mask000
    
        if let _ = oProperty1 {
            mask |= mask001
        }
        if let _ = oProperty2 {
            mask |= mask010
        }
        if let _ = oProperty3 {
            mask |= mask100
        }
    
        if mask == mask000 {
            return nil
        } else if mask == mask001 {
            let prop3 = oProperty3!
            return set.first(where: { $0.prop3 == prop3 })
        } else if mask == mask010 {
            let prop2 = oProperty2!
            return set.first(where: { $0.prop2 == prop2 })
        } else if mask == mask011 {
            let prop2 = oProperty2!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop2 == prop2 && $0.prop3 == prop3 })
        } else if mask == mask100 {
            let prop1 = oProperty1!
            return set.first(where: { $0.prop1 == prop1 })
        } else if mask == mask101 {
            let prop1 = oProperty1!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop1 == prop1 && $0.prop3 == prop3 })
        } else if mask == mask110 {
            let prop1 = oProperty1!
            let prop2 = oProperty2!
            return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 })
        } else if mask == mask111 {
            let prop1 = oProperty1!
            let prop2 = oProperty2!
            let prop3 = oProperty3!
            return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 && $0.prop3 == prop3 })
        }
        return nil
    }
    
    let exInstance1 = MyStruct(prop1: "no good", prop2: nil, prop3: "goodbye")
    let exInstance2 = MyStruct(prop1: "hello", prop2: "noproblem", prop3: "goodbye")
    let exInstance3 = MyStruct(prop1: nil, prop2: nil, prop3: "goodbye")
    
    let exampleSet: Set<MyStruct> = [
        exInstance1,
        exInstance2,
        exInstance3,
    ]
    
    if let object = findBestMatch(for: exampleSet, oProperty1: "hello", oProperty2: nil, oProperty3: "goodbye") {
        print(object) // print MyStruct(prop1: Optional("hello"), prop2: Optional("noproblem"), prop3: Optional("goodbye"))
    } else {
        print("not found")
    }