【问题标题】:Swift Cannot invoke 'find' with an argument list of type '([Score], Score)' where Score is a structSwift 无法使用类型为“([Score],Score)”的参数列表调用“find”,其中 Score 是一个结构
【发布时间】:2015-06-25 10:13:22
【问题描述】:

虽然find(["a", "b"], "c") 没有问题,但在尝试在结构数组中查找结构的索引时出现错误:

struct Score
{
    //...
}

var scores: [Score] = //...
var score: Score = //...

find(self.scores, score) // Error: Cannot invoke 'find' with an argument list of type '([Score], Score)'

虽然默认情况下无法相互比较的结构可能是个问题。但是将Scores 定义更改为class 给我同样的错误。

【问题讨论】:

标签: arrays swift find indexof


【解决方案1】:

函数find的接口是/曾经:

func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C,
    value: C.Generator.Element) -> C.Index?

这表示CCollectionType 必须具有Equatable 的元素,此外,value 也必须是Equatable

[注意 Swift 3.0:从 Swift 3.0 开始,您需要使用 index 函数,它有两种变体。首先,您将提供自己的谓词:

func index(where: (Self.Generator.Element) -> Bool) -> Self.Index?

第二,你的元素需要是平等的:

// Where Generator.Element : Equatable
func index(of: Self.Generator.Element) -> Self.Index?

如果您决定走equatable 路线,则以下内容适用。 注尾]

您的Score 结构不是Equatable,因此会出现错误。 您需要弄清楚分数彼此相等意味着什么。也许它是一些数字“分数”;也许它是一个“分数”和一个“用户 ID”。这取决于您的 Score 抽象。一旦你知道了,你就可以使用 == 来实现:

func == (lhs:Score, rhs:Score) -> Bool {
 return // condition for what it means to be equal
}

注意:如果您使用class 并且因此分数具有“身份”,那么您可以将其实现为:

func == (lhs:Score, rhs:Score) -> Bool { return lhs === rhs }

您的字符串示例有效,因为StringEquatable。如果您查看 Swift 库代码,您会看到:

extension String : Equatable {}
func ==(lhs: String, rhs: String) -> Bool

【讨论】:

    【解决方案2】:

    正如其他人所说,您搜索的对象必须符合 Equatable 协议。

    因此,您需要在 Score 结构中添加一个扩展,告诉编译器它符合该协议:

    extension Score: Equatable {}
    

    然后你需要为那个类实现 == 函数:

    public func ==(lhs: Score, rhs: Score) -> Bool
    {
      return lhs.whatever == rhs.whatever //replace with code for your struct.
    }
    

    【讨论】:

    • 为什么将Score 改为class 时会出现同样的错误? == 对课程无效吗?
    • 没有。 == 对任何自定义对象都无效,除非您按照我的回答中所述进行定义。
    【解决方案3】:

    编辑:从 Swift 2.0 开始,现在有一个内置的 find 版本,它需要一个闭包,所以你不必自己编写 - 而且,find 已重命名为 indexOf,并且现在是CollectionType 的协议扩展,所以你可以把它当成一个方法来称呼:

    // if you make `Score` conform to `Equatable:
    if let idx = self.scores.indexOf(score) {
    
    }
    
    // or if you don't make it Equatable, you can just use a closure:
    // (see original answer below for why you might prefer to do this)
    if let idx = scores.indexOf({$0.scoreval == 3}) {
    
    }
    

    下面是 2.0 之前的原始答案


    虽然建议让你的班级Equatable 的答案可能会很好地工作,但我建议在选择这样做之前要小心。原因是,正如文档所述,等式意味着可替代性,并且您的 == 运算符必须是自反的、对称的和传递的。如果您不遵守这一点,则在使用equalssort 等算法时可能会出现一些非常奇怪的行为。如果在非最终类上实现Equatable,请特别小心。如果你确定你能满足要求,那就去做吧,find 会起作用的。

    如果没有,您可以考虑的替代方法是编写一个应该在标准库中但不在标准库中的函数,即采用闭包的find

    func find<C: CollectionType>(source: C, match: C.Generator.Element -> Bool) -> C.Index {
        for idx in indices(source) {
            if match(source[idx]) { return idx }
        }
        return nil
    }
    

    一旦你有了这个,你可以提供任何你喜欢的匹配标准。例如,如果您的对象是类,您可以使用引用相等:

    let idx = find(scores) { $0 === $1 }
    

    【讨论】:

    • 看来indexOf在Swift 3中已经改名为index(of:)了。
    猜你喜欢
    • 2022-06-11
    • 1970-01-01
    • 2021-05-02
    • 2014-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-23
    相关资源
    最近更新 更多