Swift通用构造函数

时间:2015-06-15 23:13:32

标签: swift generics

问题已嵌入评论以及此处:

  1. 如何在参数列表中定义通用T的默认值?
  2. 在Swift中复制参数时,它是指针还是对象?
    • XCode Beta 7告诉我,我无法改变" head",因为它是" let",请参阅代码
  3. 任何类似Swift的方式来循环检查nil以避免展开?
  4. 链接列表实施:

    public class Node<T>
    {
        public var data: T
        public var next: Node<T>?
    
        //1.
        //How can I define a default datatype for T?
        //In C++ I would let T ctor do it: "data: T = T()" gives 
        //error: 'T' cannot be constructed because it has no accessible initializers
        public init(data: T, next: Node<T>?)
        {
            self.data = data
            self.next = next
        }
    }
    
    func print<T>(head: Node<T>)
    {
        var tmp = head  //2. Is this a copy of a pointer or an object?
        print(tmp.data)
        while(tmp.next != nil)  //3. Any Swiftier way to do it?
        {
            tmp = tmp.next!
            print(tmp.data)
        }
    }
    
    func insert<T>(head: Node<T>, _ value: T)
    {
        var tmp = head
        while tmp.next != nil
        {
            tmp = tmp.next!
        }
        tmp.next = Node<T>(data: value, next: nil)
    }
    
    var head = Node<Int>(data: 1, next: nil)
    insert(head, 2)
    insert(head, 4)
    insert(head, 8)
    insert(head, 16)
    print(head)
    

    另外,还有其他意见吗?我是Swift的新手

3 个答案:

答案 0 :(得分:8)

<强> 1

泛型是关于代码安全的,而不知道编译时的类型。这意味着将泛型类型初始化为某些东西是没有意义的。但是通过声明像

这样的协议
protocol Initializable {
    init()
}

,通过执行

扩展您想要使用的类型
extension Int : Initializable {}

(可能自己实现init()方法)并将节点声明为

public class Node<T: Initializable>

您可以在初始化程序中使用T()来创建初始值。因为现在编译器知道这种类型确实有这样的初始化器,因为它符合该协议。

<强> 2

始终作为参考传递,而结构始终复制

为了能够更改函数的输入值,必须将其声明为inout,如

func insert<T>(inout head: Node<T>, _ value: T)

然后你还需要调用像insert(&head, 2)

这样的函数

第3

您可以像

一样编写循环
while let next = tmp.next {
    tmp = next
    print(tmp.data)
}

<强> 4

Swift是一种优美的语言,我强烈建议您使用更多的Swiftier代码。

你编写这两个方法的方式就像在C中一样。你可以把函数放在类中,这是最好的方法,它也更简单。

我承认不是很重要,但这种花括号的风格已经过时了

如果您确实不需要,则无需将函数声明为public。在编写框架或类似的东西时,你只需要它。默认行为(无访问控制修饰符)是访问整个项目中的所有Swift文件。

我在这里制定的Initializable协议并不是真正的Swifty

while (boolean)周围的括号不需要,也不是Swifty

命名参数有一个原因:使代码更具可读性。只有当你完全确定它很烦人并清楚地知道参数代表什么时才删除它们。

我最初不想这样做,但我以更加Swifty的方式重新编写代码,我希望我可以帮助你:

protocol DefaultValuable {
    static func defaultValue() -> Self
}

extension Int : DefaultValuable {
    static func defaultValue() -> Int {
        return 1
    }
}

class Node<T: DefaultValuable> {
    var data: T
    var next: Node<T>?

    init(data: T = T.defaultValue(), next: Node<T>? = nil) {
        self.data = data
        self.next = next
    }

    func printNode() {
        var tmp = self
        print(tmp.data)

        while let next = tmp.next {
            tmp = next
            print(tmp.data)
        }
    }

    func insert(value: T) {
        var tmp = self

        while let next = tmp.next {
            tmp = next
        }

        tmp.next = Node<T>(data: value)
    }
}

let head = Node<Int>()
head.insert(2)
head.insert(4)
head.insert(8)
head.insert(16)
head.printNode()

(抱歉有点咆哮)

答案 1 :(得分:1)

  1. 你不能在Swift中这样做。您可以将data设为可选项,或针对特定类型重载init,但self.data = T()或类似内容不可能。

  2. 从类中创建的对象的变量在Swift中总是引用类型,所以基本上都是指针。

  3. 您的print函数可以写得更好:

    func print<T>(head: Node<T>)
    {
        var tmp: Node<T>? = head
        while let currentNode = tmp
        {
            print(currentNode.data)
            tmp = tmp?.next
        }
    }
    

答案 2 :(得分:0)

我认为带有复选标记的答案是迄今为止最好和最详细的答案。

我想要补充的是我对通用的看法。

如果我正确理解Swift中值,类型和类型构造函数之间的关系。

首先,我认为需要执行或操作的所有内容都是值,例如1,2,3"I am a string"左右。

其次,类型是值的类型。有两种类型,Reference TypeValue TypeReference Type包含class,表示值由class创建,而Value Type包含StructEnum。另外,我认为Functionclosure也是引用类型,即使它们在创建后是不可变的。 protocol怎么样,我的想法是protocol是一种类型,即使有些protocolassociatedtype时有时Self无法用作类型在protocol中使用,protocol只能在Type Constraint中用作Generic

最后一个是Generic,它是需要generic parameters的类型构造函数,具体类型,意思是GenericArray<Element>Element是{{1} } generic parameter数组,用于创建具体的Generictype,然后依次创建耗材Array<Int>value

因此,如果您想像某些类[1,2,3,4]Generic那样使用type作为普通NSString,则必须提供UILabel类型先预先得出一个具体的类型。

更新

首先,我为关于协议作为一种类型的声明道歉。这是不正确的。是的,协议可以表现得像一个类型。但实际上它是一个抽象类型的接口,描述了构造者应该实现的一组行为。当协议内部使用generic parameters和/或associatedtype时,它会获得一些通用特性,使其更适合Self constraint Type Constraint,附加条件,而且有力地扩展了它的扩展默认实现。

在一天结束时,我将where视为一个包装器,它没有自己的实例或值,但包装了其conformer类型的值,此外,protocol提供了默认行为同样。