Fast generic constructor
Questions are inserted in the comments and also here:
- How do I determine the default value for the general parameter list T?
- When copying a parameter in Swift, are they pointers or objects?
- Xcode Beta 7 tells me that I cannot change "head" as it is "let", see code
- Any Swift type method to test while-loop for nil to avoid unwrapping?
Linked list implementation:
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)
Also, any other comments? I am very new to Swift
source to share
1.
General concepts are code safety not knowing the type at compile time. This means there is no point in initializing a generic type. However, by declaring a protocol like
protocol Initializable {
init()
}
expanding the types you want to use by doing
extension Int : Initializable {}
(maybe implement the method init()
yourself) and declare your node as
public class Node<T: Initializable>
you can use T()
in your initializer to create an initial value. Because now the compiler knows that this type does indeed have such an initializer, because it conforms to this protocol.
2.
Classes are always passed as a reference, whereas structs are always copied
To be able to change the input value of a function, it must be declared inout
as
func insert<T>(inout head: Node<T>, _ value: T)
Then you also need to call the function like insert(&head, 2)
3.
You can write a loop like
while let next = tmp.next {
tmp = next
print(tmp.data)
}
4.
Swift is a pretty language and I highly recommend you use more Swiftier code in general.
The way you wrote these 2 methods is similar to C. You can put functions in classes, and this is the optimal way to do it, and it is also much easier.
Not very important, I admit, but this curly brace style is deprecated
You don't need to make a function public unless you really need it. And you only need this when writing a framework or something. The default behavior (no access control modifier) ββis to have access to all your Swift files in your entire project.
This protocol Initializable
I made here is not actually Swifty, either
Parentheses around while (boolean)
are unnecessary and are not Swifty
Named parameters exist for a reason: to make the code more readable. Only remove them when you are absolutely sure that it is annoying and it is clear what the parameter represents.
I didn't mean to do this initially, but I'm rewriting my code much more Swifty, and I hope I can help you with that:
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()
(Sorry for the peculiar ranting)
source to share
-
You cannot do this in Swift. You can make it
data
optional or overloadinit
for certain types, butself.data = T()
either is not possible. -
Variables for objects created from classes are always reference types in Swift, so mostly pointers under the hood.
-
Your function
print
could be better written:func print<T>(head: Node<T>) { var tmp: Node<T>? = head while let currentNode = tmp { print(currentNode.data) tmp = tmp?.next } }
source to share
I think the tagged answer is the best and detailed answer.
What I want to add is my view of the general.
If I understand correctly the relationship between value, type and type constructor in Swift.
First, I think all that needed to be done or manipulated were values ββlike 1,2,3
, "I am a string"
or so.
Second, types are value types. There are two types of types: Reference Type
and Value Type
. Reference Type
contains class
, which means the values ββare created with class
, whereas Value Type
contains Struct
and Enum
. Also, I think Function
u is closure
also a reference type, although they are immutable once created. How about protocol
, my idea is what protocol
is a type of type, although sometimes protocol
it cannot be used as a type, if in is protocol
used associatedtype
or Self
, then protocol
can only be used as Type Constraint
in Generic
.
The last Generic
, which is a type constructor, in need of generic parameters
a particular type of meaning Generic
, Array<Element>
and Element
is generic parameter
of the Generic
Array, is used to create the concrete type
, Array<Int>
and then, in turn, create a consumable value
, [1,2,3,4]
.
Therefore, if you want to use Generic
as plain type
as some classes NSString
, UILabel
or so, you must specify both types generic parameters
for a specific type of advance.
Update
First of all, I apologize for my statement about the type of protocol. This is not true. Yes, the protocol can behave like a type. But it is actually an abstract type interface that describes a group of behavior conformers that must be implemented. When some associatedtype
and / or are used in the protocol Self constraint
, it has gained some general characteristic that allows it to be more correct as Type Constraint
c where
, an additional condition has also powerfully extended its default implementation by default.
At the end of the day, I see protocol
as a wrapper that doesn't have its own instance or value, but wraps the value of its own connector type, and protocol extension
also offers default behavior.
source to share