patternswiftMinor
A generic AVL tree with SequenceType, CollectionType and ArrayLiteralConvertible extensions
Viewed 0 times
genericsequencetypecollectiontypeextensionswitharrayliteralconvertibleavlandtree
Problem
I'm learning Swift and trying get a good understanding of some of its many features. The following is an AVL tree implementation with extensions that conform to
Everything seems to work correctly. I wish to get some feedback on my overall approach, pointers on how I can improve it and conventions I should be following.
```
protocol BinarySearchTreeType {
associatedtype Element
mutating func insert(element: Element)
mutating func find(element: Element) -> Element?
}
class Node {
var value: Element
var leftNode: Node?
var rightNode: Node?
init(value: Element) {
self.value = value
}
var height: Int {
let left = leftNode != nil ? leftNode!.height + 1 : 0
let right = rightNode != nil ? rightNode!.height + 1: 0
return max(left, right)
}
}
enum BinarySearchTreeError: ErrorType {
case OutOfBound
}
final class BinarySearchTree: BinarySearchTreeType {
private var _root: Node?
private var _count: Int = 0
var root: Node? {
return _root
}
var count: Int {
return _count
}
/**
Inserts an element into the AVL tree. Duplicate elements are ignored.
- parameter element: Element which will be added to the tree.
It must conform to Comparable.
*/
func insert (element: Element) {
if let _ = _root {
self.insert(element, currentNode: &_root!)
} else {
_root = Node(value: element)
_count += 1
}
}
private func insert(element: Element, inout currentNode: Node) -> Node {
if currentNode.value > element {
if currentNode.leftNode != nil {
currentNode.leftNode = insert(element, currentNode: ¤tNode.leftNode!)
} else {
currentNode.leftNode = Node(value: element)
_count += 1
}
if height(currentNode.leftNode) - height(currentNode.rightNode) == 2 {
if element (value: element)
_count += 1
}
if height(currentNode.rightNode) - height(currentNode.le
SequenceType, CollectionType and ArrayLiteralConvertibleEverything seems to work correctly. I wish to get some feedback on my overall approach, pointers on how I can improve it and conventions I should be following.
```
protocol BinarySearchTreeType {
associatedtype Element
mutating func insert(element: Element)
mutating func find(element: Element) -> Element?
}
class Node {
var value: Element
var leftNode: Node?
var rightNode: Node?
init(value: Element) {
self.value = value
}
var height: Int {
let left = leftNode != nil ? leftNode!.height + 1 : 0
let right = rightNode != nil ? rightNode!.height + 1: 0
return max(left, right)
}
}
enum BinarySearchTreeError: ErrorType {
case OutOfBound
}
final class BinarySearchTree: BinarySearchTreeType {
private var _root: Node?
private var _count: Int = 0
var root: Node? {
return _root
}
var count: Int {
return _count
}
/**
Inserts an element into the AVL tree. Duplicate elements are ignored.
- parameter element: Element which will be added to the tree.
It must conform to Comparable.
*/
func insert (element: Element) {
if let _ = _root {
self.insert(element, currentNode: &_root!)
} else {
_root = Node(value: element)
_count += 1
}
}
private func insert(element: Element, inout currentNode: Node) -> Node {
if currentNode.value > element {
if currentNode.leftNode != nil {
currentNode.leftNode = insert(element, currentNode: ¤tNode.leftNode!)
} else {
currentNode.leftNode = Node(value: element)
_count += 1
}
if height(currentNode.leftNode) - height(currentNode.rightNode) == 2 {
if element (value: element)
_count += 1
}
if height(currentNode.rightNode) - height(currentNode.le
Solution
I have a major point of criticism regarding the "Swifty-ness" of your code:
There is far too much "forced unwrapping" of optionals, and explicit comparing against
This can almost always be avoided by using
See also When should I compare an optional value to nil?
on Stack Overflow.
Example 1:
should be written with optional binding as
It is even shorter because
the
Example 2:
can be simplified with optional chaining and the nil-coalescing operator
Example 3:
Again forced unwrapping, and
The above transformations do not work directly in this case
because
But actually the
does not really need an
tree after the insertion. It can be rewritten as
And now the
The same principle can be applied at many places in your code. I hope that the above
examples can serve as a starting point to clean-up the handling of optionals.
The "public readonly, private read-write" properties "root" and "count" can be
defined as
which makes the additional accessor methods obsolete. Actually
publicly visible at all.
The
the value makes no sense after a node as been inserted in the tree.
My suggestion would be to fix the "optional handling" first and then review the code in a second iteration.
There is far too much "forced unwrapping" of optionals, and explicit comparing against
nil.This can almost always be avoided by using
- Optional binding:
if/while let myValue = myOptional { ... },
- optional chaining:
myOptional?.property, or
- the nil-coalescing operator:
myOptional ?? defaultValue.
See also When should I compare an optional value to nil?
on Stack Overflow.
Example 1:
func minNode(root: Node) -> Node {
var _current = root
while _current.leftNode != nil {
_current = _current.leftNode!
}
return _current
}should be written with optional binding as
func minNode(root: Node) -> Node {
var _current = root
while let lNode = _current.leftNode {
_current = lNode
}
return _current
}It is even shorter because
the
leftNode property is accessed only once per loop iteration.Example 2:
private func height (node: Node?) -> Int {
return node != nil ? node!.height : -1
}can be simplified with optional chaining and the nil-coalescing operator
??:private func height (node: Node?) -> Int {
return node?.height ?? -1
}Example 3:
func insert(element: Element) {
if let _ = _root {
self.insert(element, currentNode: &_root!)
} else {
_root = Node(value: element)
_count += 1
}
}Again forced unwrapping, and
if let _ = _root is just if _root != nil in disguise.The above transformations do not work directly in this case
because
self.insert(element, currentNode: &_root!) modifies the last argument.But actually the
private func insert(element: Element, inout currentNode: Node) -> Node {
// ...
return currentNode
}does not really need an
inout parameter because it returns the new root of the tree after the insertion. It can be rewritten as
private func insert(element: Element, currentNode: Node) -> Node {
var currentNode = currentNode // make mutable copy
// ...
return currentNode
}And now the
insert(element: Element) method can use optional binding as well:func insert(element: Element) {
if let node = _root {
_root = self.insert(element, currentNode: node)
} else {
_root = Node(value: element)
count += 1
}
}The same principle can be applied at many places in your code. I hope that the above
examples can serve as a starting point to clean-up the handling of optionals.
The "public readonly, private read-write" properties "root" and "count" can be
defined as
private(set) var root: Node?
private(set) var count: Int = 0which makes the additional accessor methods obsolete. Actually
root need not bepublicly visible at all.
The
value property of Node should be declared as a constant because modifyingthe value makes no sense after a node as been inserted in the tree.
My suggestion would be to fix the "optional handling" first and then review the code in a second iteration.
Code Snippets
func minNode(root: Node<Element>) -> Node<Element> {
var _current = root
while _current.leftNode != nil {
_current = _current.leftNode!
}
return _current
}func minNode(root: Node<Element>) -> Node<Element> {
var _current = root
while let lNode = _current.leftNode {
_current = lNode
}
return _current
}private func height (node: Node<Element>?) -> Int {
return node != nil ? node!.height : -1
}private func height (node: Node<Element>?) -> Int {
return node?.height ?? -1
}func insert(element: Element) {
if let _ = _root {
self.insert(element, currentNode: &_root!)
} else {
_root = Node(value: element)
_count += 1
}
}Context
StackExchange Code Review Q#133349, answer score: 4
Revisions (0)
No revisions yet.