【问题标题】:Swift Tuples - Different from struct and from each other?Swift Tuples - 与结构和彼此不同?
【发布时间】:2015-02-07 15:39:38
【问题描述】:

swift 中的元组与结构有何不同? (1)

据我了解,元组和结构都可以按值发送,而不是在函数调用、返回中通过引用发送,对吧?

另外,我知道如果有

var A : StructureX
var B : StructureX

我知道结构A和B有相同的Type,也就是StructureX。但是……

let A : (Int, String)
let B : (Int, String)

A 和 B 元组是否相同 Type? (2) 使用元组而不是结构有什么好处? (3)

【问题讨论】:

    标签: swift tuples


    【解决方案1】:

    我不确定有关元组的官方术语,但是您将它们声明为好像它们是一种特殊类型:

    let A : (Int, String)
    

    也许我们可以说 A 现在是元组类型的变量?然而,并不是所有的元组都是一样的。如果您声明元组类型的变量并尝试为其分配一个元组,其参数的计数、序列或类型不同,您将收到编译器错误。这将失败

    let A : (Int, String) = ("Bob", 1234, 4.0) 
    

    虽然这很好用:

    let A : (Int, String) = (1234, "Bob")
    

    当然,这种强类型安全性也可以通过结构来实现。

    就优势而言,这里是我所知道的差异的一些想法。

    结构要求您在使用它们之前对其进行定义。另一方面,元组允许您返回任意值列表。这有什么用?我有一个 iPad 应用程序,里面有一个购物车视图控制器。购物车视图中有一个摘要视图,可显示任何给定时间购物车中物品的当前状态 - 有时只是普通物品,但 RMA 物品和重新订购的物品也可能在购物车中。我的购物车类有一个方法,它返回一个包含购物车计数、RMA 计数、重新订购计数和总计数的元组。我不必声明一个结构来取回所有四个值。很方便:

    class Cart : NSManagedObject {
        ...
        var totals : (cartCount:Int, rmaCount:Int, reorderedCount:Int, totalCount:Int) {
            let cart = ...    // Calculate cart count
            let rma = ...     // Calculate rma count
            let reorder = ... // Calculate reorder count
            let total = cart + rma + reorder // Add them all up
    
            return (cart, rma, reorder, total)
        }
    }
    

    在我的购物车视图中:

    let cartValues = cart.totals
    self.summaryView.cartCountLabel.text = "\(cartValues.cartCount)"       
    self.summaryView.rmaCountLabel.text = "\(cartValues.rmaCount)"    
    self.summaryView.reorderCountLabel.text = "\(cartValues.reorderedCount)"    
    self.summaryView.totalCountLabel.text = "\(cartValues.totalCount)"
    

    可能还有其他原因,但对我来说,在这种情况下更喜欢元组是最有说服力的原因。

    【讨论】:

    • 好像很方便。尽管只要我们不断升级软件,如果我们不使用带有命名成员的元组,它也会产生一些错误。如果我们保证每个人都在命名它,那应该没问题。非常感谢您的回答!
    【解决方案2】:

    我发现将 Swift 元组概念化为“匿名结构”是最容易的,但有一些关键区别。它们的行为相似,但结构有正式的定义并允许对可变性进行更多控制,而元组允许模式匹配。

    元组和结构之间的相似之处

    • 两者都可以有任意数量的任意类型的成员,包括闭包
    • 两者都可以内联构造(参见下面代码中的typealias
    • 如果声明为常量,两者都可以防止任何成员发生突变
    • 如果元组具有标签成员,则结构和元组都允许通过标签访问成员

    元组和结构的区别

    • 结构体在使用前需要定义
    • 结构不允许对其成员进行模式匹配
    • 如果实例是变量,结构体允许声明为变量的成员可变
    • 元组不允许变异函数或引用其任何成员的函数
    • 元组可能无法实现协议
    • 如果元组有匿名成员,则可以通过索引访问其成员,这与结构不同

    一些说明这些差异和相似之处的游乐场代码

    // All commented code causes a compilation error. Uncomment to view error messages.
    
    struct StructureX {
        let a: Int = 0
        var b: String = "string"
    }
    
    //
    // Struct member variability
    //
    var structureA: StructureX = StructureX()
    let structureB: StructureX = StructureX()
    //structureA.a = 2              // declared as a constant, instance is variable
    structureA.b = "allowed"        // declared as a variable, instance is variable
    //structureB.a = 2              // declared as constant, instance is constant
    //structureB.b = "not allowed"  // declared as constant, instance is constant
    structureA = structureB         // these are the same type
    structureA
    
    
    //
    // A tuple can't be used as a literal to construct a struct.
    //
    //let StructureC: StructureX = (a: 17, b: "nope")
    
    
    //
    // Typealias a labeled tuple and it can be constructed similarly to a struct
    //
    typealias StructureT = (a: Int, b: String)
    var structureD: StructureT = StructureT(a: 0, b: "asdf")
    structureD
    //structureD = structureA       // but they are distinct types
    
    
    
    let emptyTuple: () = ()         // philosophically, isn't this the definition of Void?
    print(emptyTuple)               // prints as ()
    let single: (Int) = (23)
    //let namedSingle: (a: Int) = (a: 42)
    
    
    //
    // Tuple Labeled Member Access
    //
    var labeledTupleA: (a: Int, b: String) = (a: 0, b: "string")
    labeledTupleA.0 = 5
    labeledTupleA.a
    labeledTupleA
    
    var check: (a: Int, b: String)
    check = labeledTupleA           // same type
    check
    
    //
    // Tuples can have functions/closures
    //
    let labeledTupleB: (Int, String, fun: () -> Void) = (0, "string", { () -> Void in
        print("hi")
    })
    labeledTupleB.1
    labeledTupleB.fun()
    //labeledTupleB.0 = 10          // this tuple is a constant, so all of its members are constant
    
    
    //
    // Tuples with members of the same type, but differet labels are not of the same type
    //
    var labeledTupleC: (c: Int, d: String) = (c: -1, d: "fail")
    //labeledTupleC = labeledTupleA
    //labeledTupleC = labeledTupleB
    
    
    //
    // Tuples with anonymous members matching the type pattern of a labeled member tuple are of equivalent type
    //
    var unlabeledTuple: (Int, String) = (0, "good")
    unlabeledTuple = labeledTupleA
    unlabeledTuple = labeledTupleC
    
    
    //
    // Tuples with closures may not refer to sibling members
    //
    var labeledTupleD: (de: Int, df: (Int) -> Void) = (de: 0, df: { (num: Int) -> Void in
        //de += num
        //self.de += num
        print(num)
    })
    
    labeledTupleD.de
    labeledTupleD.df(1)
    
    
    //
    // Tuples allow pattern matching, Structs do not
    //
    //switch structureA {
    //case (let i, let s):
    //    print(i, s)
    //default:
    //    break
    //}
    
    switch labeledTupleD {
    case (_, let closure):
        closure(123)
    default:
        break
    }
    

    【讨论】:

    • 这对我来说似乎很不错。虽然我仍然看不到带有 匿名成员 的元组有多好(特别是索引成员,这似乎是非常糟糕的编程),但我不知道模式匹配。我现在要看看这个!
    • 随着最佳实践的出现,这似乎将在未来几年内定义。目前,我认为它们对于在几个小地方轻松返回多个值很有用,而无需定义只使用一次的结构。如果我发现自己在多个地方使用匿名元组,我会将其形式化为结构。
    • 我遇到的元组的最大问题是您不能定义一个名为元组的值。听起来很傻,但在某些情况下它可能有用,主要是为了保持一致性。
    • 这是什么意思:“两者都可以内联构造”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-17
    • 2021-06-07
    • 2013-10-12
    相关资源
    最近更新 更多