问题已嵌入评论以及此处:
链接列表实施:
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的新手
答案 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)
你不能在Swift中这样做。您可以将data
设为可选项,或针对特定类型重载init
,但self.data = T()
或类似内容不可能。
从类中创建的对象的变量在Swift中总是引用类型,所以基本上都是指针。
您的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 Type
和Value Type
。 Reference Type
包含class
,表示值由class
创建,而Value Type
包含Struct
和Enum
。另外,我认为Function
和closure
也是引用类型,即使它们在创建后是不可变的。 protocol
怎么样,我的想法是protocol
是一种类型,即使有些protocol
或associatedtype
时有时Self
无法用作类型在protocol
中使用,protocol
只能在Type Constraint
中用作Generic
。
最后一个是Generic
,它是需要generic parameters
的类型构造函数,具体类型,意思是Generic
,Array<Element>
和Element
是{{1} } generic parameter
数组,用于创建具体的Generic
,type
,然后依次创建耗材Array<Int>
,value
。
因此,如果您想像某些类[1,2,3,4]
,Generic
那样使用type
作为普通NSString
,则必须提供UILabel
类型先预先得出一个具体的类型。
首先,我为关于协议作为一种类型的声明道歉。这是不正确的。是的,协议可以表现得像一个类型。但实际上它是一个抽象类型的接口,描述了构造者应该实现的一组行为。当协议内部使用generic parameters
和/或associatedtype
时,它会获得一些通用特性,使其更适合Self constraint
Type Constraint
,附加条件,而且有力地扩展了它的扩展默认实现。
在一天结束时,我将where
视为一个包装器,它没有自己的实例或值,但包装了其conformer类型的值,此外,protocol
提供了默认行为同样。