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

+3


source to share


3 answers


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)

+8


source


  • You cannot do this in Swift. You can make it data

    optional or overload init

    for certain types, but self.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
        }
    }
    
          



+1


source


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.

0


source







All Articles