Value type confusion

151 views Asked by At

My first question on StackOverflow :)

I started to learn Swift in XCode a few days ago and I'm having some trouble understanding the value type in Swift. Since I'm quite familiar with Java, I'm just going to explain my question in Java. Here's what I want to write in Swift.

class Node {
    public int value; 
    public List<Integer> children;
    public Node parent;

    public Node() {
        value = 0;
        children = new ArrayList<Integer>();
    }

    public Node bestChild() {
        Node result = new Node();
        for (child : this.children) {
            if (child.value > result.value)
                result = child;
        }
        return result;
    }
}

For the sake of this question, let's not talk about private, public, or generic here. Just keep it simple! Here's my try in Swift.

public class Node {
    var value: Int
    var children: NSSet
    var parent: Node?

    public init() {
        self.value = 0
        children = NSSet()
    }

    public func bestChild() -> Node {
        var result = Node()
        for child in children {
            if (child.value > result.value) {      //error
                result = child                     //error
            }
        }
        return result
    }
}

Both the condition for the if statement and the line inside have errors. XCode suggested casting the variables but still it didn't solve the errors.

if ((child as AnyObject).value > result.value) {
    result = child as! Node
}

Since Java is the only language I know, if someone could explain it in term of Java I would greatly appreciate it. If not, please keep it simple so I can wrap my head around it :)

Thanks in advance!

1

There are 1 answers

3
Alexander On BEST ANSWER

welcome to Stack Overflow and Swift programming!

Improvements to the Java code

Firstly, I suggest some changes to the Java code.

  1. Use static initializers where possible. After doing this, you don't even need an explicit constructor.
  2. Your children variable is type List<Integer>, but I suspect you meant it to be List<Node>.
  3. Don't create a new Node just for comparison's sake. With this approach, if bestChild() returns a Node with a value of 0, it can't be known if that's because the children list was empty, of if there really was a Node with value 0 that was the highest value. I would use the stream API, though you could manually implement such functionality.

Here is the end result:

class Node {
    public int value;
    public List<Node> children = new ArrayList<>();
    public Node parent; // Unused?

    public Node bestChild() {
        // Modern, easy approach:
        return children
               .stream()
               .max((x, y) -> Integer.compare(x.value, y.value))
               .get();

        // Old, classic Java approach:
        /*
        int max = Integer.MIN_VALUE;
        Node maxNode = null;

        for (Node child : this.children) {
            if (max < child.value) {
                max = child.value;
                maxNode = child;
             }
        }

        return maxNode; */
    }
}

The Swift equivalent

Here's how I would write this in Swift. Feel free to ask follow up questions

class Node {
    let value = 0
    let children = [Node]()
    weak var parent: Node? // Unused?

    func bestChild() -> Node? {
        return children.max(by: { $0.value < $1.value })
    }
}

The Swift equivalent of the antiquated Java apporach

Here's the Swift implementation of the antiquated Java approach to the bestChild method. This is just for comparison's sake. DON'T DO THIS!

func bestChild() -> Node? {
    var max = Int.min
    var maxNode: Node?

    for child in children {
        if max < child.value {
            max = child.value
            maxNode = child
        }
    }

    return maxNode
}

To address the issues in your solution attempt:

NSSet is to Swift, as HashSet (without the generic type parameter, not HashSet<T>) is to Java. It's a datatype from the Foundation library, that was invented for Objective-C. You shouldn't be using it in Swift in 99.999% of cases. NSSet acts as a set of AnyObject. In order to be able to call .value on an object from the set, you need to first manually cast the object to Node.

The solution to all that is to instead use Set<Int>, which is Swift's native Set data structure. Though this raises the question, why are you using a Set in the swift solution, but an array in the Java solution?