I need to have custom logic in a Set that defines when a Hashable can be insert or not.
First I tried to solve this with a observer
var Tenants: Set<Tenant> = [] {
willSet {
// to the business logic here
// ...
But in an observer i can not return an error. So I tried to extend Set to overwrite the insert method.
extension Set where Element == Tenant {
#inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
// .... do my logic here ...
return (true, newMember)
}
}
That works so far and the method will be called. I can return true and if my logic did not pass even a false. Ok, but how do I add the Element into the Set? super.insert(). The return is correct, but the Set is empty. How to add the elements into the concrete set?
Implementation so far
/// Global set of known tenants
var Tenants: Set<Tenant> = [] {
willSet {
let newTenants = newValue.symmetricDifference(Tenants)
guard let newTenant = newTenants.first else {
Logging.main.error("Can not find tenant to add.")
return
}
Logging.main.info("Will add new Tenant \(newTenant.name) [\(newTenant.ident)]")
}
}
extension Set where Element == Tenant {
#inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
print("Check to add...")
// .... do my logic here ...
// ok
return (true, newMember)
}
}
The result is:
Check to add...
error : Can not find tenant to add.
Check to add...
error : Can not find tenant to add.
This seems to work for "do my logic here"
self = self.union([newMember])
Edit: Because this breaks the semantics of Set, I think it is better to write it as something like this:
struct CheckedSet<T: Hashable> {
private(set) var wrappedSet: Set<T> = []
var shouldInsert: (T) -> Bool = { _ in true }
mutating func maybeInsert(_ t: T) {
guard shouldInsert(t) else { return }
wrappedSet.insert(t)
}
}
var cs = CheckedSet<String>()
cs.shouldInsert = { str in str.allSatisfy(\.isLowercase) }
cs.maybeInsert("HELLO")
cs.wrappedSet // []
cs.maybeInsert("hello")
cs.wrappedSet // ["hello"]
I would do it with a property wrapper:
#propertyWrapper
struct TenantsSet {
var wrappedSet: Set<Tenant>
struct Projected {
let error: Bool
}
var projectedValue = Projected(error: false)
var wrappedValue: Set<Tenant> {
get { wrappedSet }
set {
print("some custom logic")
// set projectedValue appropriately
wrappedSet = newValue
}
}
init(wrappedValue: Set<Tenant>) {
wrappedSet = wrappedValue
}
}
This allows error-reporting by checking the error property on the projected value:
#TenantsSet var tenants = []
func f() {
tenants = [Tenant()]
if $tenants.error {
}
}
As the Swift Guide says:
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
You are not supposed to use them to modify existing behaviour. It would be very confusing to readers of your code. If you want to use an extension to do this, you should declare a new method, with a different signature. Perhaps call it insert(newTenant:)?
I would like to iterate in my code over the Swift AST like this, finding the struct keyword.
private func recursion(node: Syntax) -> String {
for child in node.children {
if let tokenKind = (child as? TokenSyntax)?.tokenKind, tokenKind == .structKeyword {
// if type(of: child) == StructDeclSyntax.self {
print ("yeah")
}
recursion(node: child)
}
}
let input = """
public struct cmd_deleteEdge<E: VEdge> : EdgeCommand {
public var keyEquivalent = KeyEquivalent.none
public let title = "Delete Edge"
public let id = "deleteEdge"
public let toolTip = "Delete selected Edge"
public let icon = Icon.delete
//receivers
public let edge: E
public init(edge: E) {
self.edge = edge
}
public func execute() throws -> ActionResult {
edge.deleteYourself()
return .success("deleted edge")
}
}
"""
public func convert(structText: String) throws -> String {
let sourceFile = try SyntaxParser.parse(source: structText)
let result = recursion(node: Syntax(sourceFile))
return result
}
try convert(structText: input)
It just simply doesn't work, I never reach the "Yeah" (which means I cannot do anything useful during the recursion).
I find this library very confusing. Would anyone have a good UML diagram explaining how it really works?
Before you tell me, yes I know I could use a Visitor, but I want to understand how it works by myself.
You can use SyntaxProtocol for iterating all items in AST and then use its _syntaxNode public property to make a target syntax, e.g.:
import SwiftSyntax
import SwiftSyntaxParser
func recursion(node: SyntaxProtocol) {
if let decl = StructDeclSyntax(node._syntaxNode) {
print(decl.identifier)
}
node.children.forEach { recursion(node: $0) }
}
let code = """
struct A {}
class Some {
struct B {}
}
func foo() {
struct C {}
}
"""
let sourceFile = try SyntaxParser.parse(source: code)
recursion(node: sourceFile)
Outputs:
A
B
C
NOTE: It is not recommended to retrieve _syntaxNode property directly and you can use Syntax(fromProtocol: node) instead.
SyntaxVisitor
But the best approach is using Visitor pattern with SyntaxVisitor class to avoid recursion issues for large and complex files:
class Visitor: SyntaxVisitor {
var structs = [StructDeclSyntax]()
init(source: String) throws {
super.init()
let sourceFile = try SyntaxParser.parse(source: source)
walk(sourceFile)
}
// MARK: - SyntaxVisitor
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
structs.append(node)
return .skipChildren
}
}
let visitor = try Visitor(source: code)
visitor.structs.forEach {
print($0.identifier)
}
I found it after trial & error and reviewing of the API.
private func recursion(node: Syntax) -> String {
for child in node.children {
if let token = TokenSyntax(child), token.tokenKind == .structKeyword {
print ("yeah")
}
recursion(node: child)
}
return node.description
}
This approach to identify the kind of the token works, and the print statement will be reached. Again, I do wonder how the class diagram for SwiftSyntax would look like.
I have a dictionary with the following structure: [Point:[Line]], where:
Point - custom data structure that contains two coordinates (X, Y)
Line - tuple (Point, Point) that contains the first and last points of the line.
Key - first point of the line.
So, it is a dictionary of lines grouped by their first point, like following:
[a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ]
The goal is to convert this dictionary into a list of data structures like following:
(a,b) -> (b,c) -> (c,d)
(a,b) -> (b,d)
(a,c) -> (c,d)
So, basically a tree with the root at the first point.
I tried to use builder pattern and stack to perform this operation, so I created a builder using first point and put in into stack, then started a while loop until stack is empty and then in the loop was removing current builder and creating new ones based on last & first points, the code looked like following:
import Foundation
typealias ProcessedLine = (UUID, [LineModel])
typealias LinePointDict = [Point: [Line]]
class LineAggregator {
var builderStack: Stack<LineModel.LineModelBuilder>
let startPointMap: LinePointDict
var result: ProcessedLine
var currentBuilder: (Point, LineModel.LineModelBuilder)?
let startPoint: Point
init(LineUid: UUID, startPointMap: LinePointDict, startPoint: Point) {
self.builderStack = Stack<LineModel.LineModelBuilder>()
self.startPointMap = startPointMap
self.result = (LineUid, [])
self.startPoint = startPoint
self.currentBuilder = nil
}
func getLineAggregation() -> ProcessedLine {
for Line in startPointMap[startPoint]! {
var bldr = LineModel.LineModelBuilder(initialLineUuid: result.0)
bldr = bldr.addLine(Line: Line)
builderStack.push(bldr)
}
return aggregateModels()
}
func aggregateModels() -> ProcessedLine {
while !builderStack.isEmpty() {
takeBuilderFromStack()
aggregateLine()
}
return result
}
/**
* This functions pops Builder object from stack if the stack is not empty and sets it as a Current object to be processed.
* #param object
* #return
*/
private func takeBuilderFromStack() {
if(!builderStack.isEmpty()) {
let curBuilder = builderStack.pop()!
currentBuilder = (curBuilder.getLastElement(), curBuilder)
}
}
private func aggregateLine() {
if currentBuilder?.1.isLastAdded() ?? true {
//If there is only one Line in the Line model
if(currentBuilder!.1.isLastAddedLineLast()) {
result.1.append(currentBuilder!.1.build())
return
}
if(!builderStack.isEmpty()) {
print("ERROR: Empty builder stack! Such situation should not happen. Pay attention at it.");
builderStack.removeAll()
}
return
}
if currentBuilder != nil {
for Line in startPointMap[currentBuilder!.0]! {
var newBuilder = LineModel.LineModelBuilder(builder: currentBuilder!.1)
newBuilder = newBuilder.addLine(Line: Line)
if Line.isLast {
result.1.append(newBuilder.build())
} else {
builderStack.push(newBuilder)
}
}
}
}
}
This solution is a very straightforward one. It works, but I have a very large amount of data in the dictionary, and the number of combinations is even larger, so this algorithm is extremely slow and not memory efficient.
The main slowness is caused by adding and retrieving data to/from stack which has following implementation:
import Foundation
protocol Stackable {
associatedtype Element
func peek() -> Element?
mutating func push(_ element: Element)
#discardableResult mutating func pop() -> Element?
}
extension Stackable {
var isEmpty: Bool { peek() == nil }
}
struct Stack<Element>: Stackable where Element: Equatable {
private var storage = [Element]()
func peek() -> Element? { storage.first }
mutating func push(_ element: Element) { storage.append(element) }
mutating func pop() -> Element? { storage.popLast() }
func size() -> Int { storage.count }
func isEmpty() -> Bool { storage.isEmpty }
mutating func removeAll() { storage.removeAll() }
}
extension Stack: Equatable {
static func == (lhs: Stack<Element>, rhs: Stack<Element>) -> Bool { lhs.storage == rhs.storage }
}
extension Stack: CustomStringConvertible {
var description: String { "\(storage)" }
}
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Self.Element...) { storage = elements }
}
And another bottleneck is related to copying data and deinit method.
I was trying to find a better solution, but couldn't find anything yet. Would be grateful for any suggestions. Thanks.
While the builder pattern is useful, I think in this case it just complicates the straight-forward solution, although as you'll see, I'll present a couple that are more complicated, but those are based on increased performance optimizations on the first simple solution.
As you you noted, initializing and deinitializing classes is kind of slow. Actually the worst part is the dynamic memory allocation. Classes are powerful and definitely have their uses, but they're not the fastest tool in the Swift toolbox. Unless you make methods final, calling them can require a virtual dispatch. That can happen with protocols too depending on the particulars of their declaration, though in that case it's called "witness table thunking". But the worst part about classes is that their instances can be littered pretty much anywhere in memory. That's hell on the processor's on-chip cache. So for performance try to avoid dynamic dispatch, and reference types (ie, classes), and when you do need to allocate memory (such as Array or Dictionary), try to allocate all you need at once, and reuse it as much as possible. In Swift that requres some thought because of copy-on-write. You can easily end up allocating memory when you didn't intend to.
I present three solutions. Each one is more complicated, but also (hopefully) faster than the previous one. I'll explain why, and what to look out for. I should mention that I am not including the simplest solution. It is much like my first solution but with local variable arrays. The performance would not be especially good, and you're question makes it clear that performance is an issue.
First there's some boiler plate. To code it up and test it, I needed to define your Point and Line types, plus a few others I use for convenience, as well as some extensions purely for generating output. This code is common to all three solutions. Substitue your own definitions for Point and Line.
struct Point: Hashable
{
// This is just to give the points uniqueness, and letter names
static private let pointNames = [Character]("abcdefghijklmnopqrstuvwxyz")
static private var curNameIndex = 0
static private var nextID: Character
{
defer { curNameIndex += 1 }
return pointNames[curNameIndex]
}
let id = nextID
}
typealias Line = (Point, Point)
typealias Graph = [Point: [Line]]
typealias Path = [Line]
// Now we add some extensions for convenient output
extension Point: CustomStringConvertible {
var description: String { "\(id)" }
}
extension String.StringInterpolation
{
mutating func appendInterpolation(_ line: Line) {
appendLiteral("(\(line.0),\(line.1))")
}
mutating func appendInterpolation(_ lines: [Line]) {
appendLiteral(lines.map { "\($0)" }.joined(separator: "->"))
}
}
You mention that Point has an X and Y, but for this problem it doesn't matter. You just need unique "things" to serve as end-points for your Line instances.
Then I declared the inputs from your example:
let (a, b, c, d) = (Point(), Point(), Point(), Point())
let input = [ a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ]
With the common code out of the way, here are the actual solutions:
Solution 1
Although the recursion introduces overhead all by itself, the main problem with the most straight-forward solution is that it requres local arrays that are allocated and deallocated up and down the call stack. The dynamic memory allocations and deallocations for them are actually the main performance problem with the simple recursive solution.
The solution is to attempt to pre-allocate working storage, and re-use it all through-out the recursion, or at least make reallocations rare.
One way would be to allocate the working arrays at the top level and pass them in as inout parameters. Another is to make them mutable properties of a struct (actually, a class wouldn't be too bad in this case, because you only allocate one instance). I chose the latter approach.
As I mentioned in my comment, I think of this problem as a graph theory problem.. Point = node. Line = edge. Though it's not explicitly stated, I assume there are no cycles. Putting in code to detect cycles isn't that hard, but would complicate the solution slightly. I also assume that the output should not contain entries with just one Line, because your example doesn't include any examples of that.
struct PathFinderVersion1
{
private let graph: Graph
private var pathList = [Path]()
private var currentPath = Path()
private init(for graph: Graph)
{
self.graph = graph
self.pathList.reserveCapacity(graph.count)
self.currentPath.reserveCapacity(graph.count)
}
static func pathList(for graph: Graph) -> [Path]
{
var pathFinder = Self(for: graph)
return pathFinder.makePathLists()
}
private mutating func makePathLists() -> [Path]
{
for src in graph.keys
{
for edge in graph[src]!
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
return pathList
}
private mutating func appendAllPaths()
{
assert(currentPath.count > 0, "currentPath must not be empty on entry")
guard let src = currentPath.last?.1 else { return }
guard let edges = graph[src] else
{
if currentPath.count > 1 {
pathList.append(currentPath)
}
return
}
for edge in edges
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
}
Apart from the init, and static wrapper function, pathList(for:), the algorithm is really just two functions. The initializer is where I pre-allocate the working storage. Assuming there is an entry in the graph Dictionary for each Point, no path can ever be longer than there entries keys in the graph ... at least not without cycles, so currentPath is initialized with that much capacity. Similar thinking applies to the other working arrays. The pathList is likely to be be larger than graph.count, but unless there are a lot of unconnected Lines, it will need to be at least as big as graph is.
makePathLists() is the part that gets thing started, extracting the Point and array of Line for each of its entries. It initializes the first entry in a currentPath, then calls appendAllPaths() to recursively append new Line instances to currentPath on the way down. When it reaches the end, it has found a complete path, so it adds the currentPath to the pathList. On the way back up, it removes the entry it added from currentPath so that it's in a state that it can be reused again to go down another path in the graph.
Once makePathLists() has iterated over all its keys and appendAllPaths() has recursed for each one, pathList contains the result. I'm not 100% sure it's in the format you want. It's basically a flat list of all the paths it found. So it's kind of one data structure. But all the paths will be grouped together according to the starting point in the line, so splitting it into smaller lists is easy enough, if that's what you actually want.
In any case, the re-use of existing storage is where this version gets most of its performance.
You can call it like this:
print("Version 1")
for path in PathFinderVersion1.pathList(for: input) {
print("\(path)")
}
And here's the output for the data I set up as input:
Version 1
(b,c)->(c,d)
(a,b)->(b,c)->(c,d)
(a,b)->(b,d)
(a,c)->(c,d)
The exact order changes slightly from run-to-run because Dictionary doesn't necessarily hand out its keys in an order that is consistent from run to run, even if they are inserted exactly the same way every time. I tested each version to verify they all emit the same output (and so should you), but I won't include the output again for the other versions.
Solution 2
This version is based on solution 1, so everything about it applies to this version too. What's different is the addition of the dynamic programming technique of caching intermediate values. Basically I cache paths along the way, so that when I encounter them again, I don't have do all that recursion. I can just used the cached path instead.
There is one snag with this caching, it requres allocating some local caches, which introduces dynamic memory allocation again. However hope is not lost. For starters, assuming lots of nodes in the graph have multiple input edges (ie, lots of different lines connect to the same line), the result should be a win overall from being able to avoid a vast amount of recursion. Additionally i, re-use the local caches, so I only ever have to actually allocate a new one when I recurse deeper than the previous maximum depth reached. So while some allocation does happen, it's minimized.
All that cache handling makes the code longer, but faster.
To make the code more readable I put the local caching in nested struct. Here's the code for version 2:
struct PathFinderVersion2
{
private typealias CachedPath = Path.SubSequence
private typealias CachedPaths = Array<CachedPath>
private let graph: Graph
private var pathList = [Path]()
private var currentPath = Path()
private var pathCache = [Point: CachedPaths]()
private struct LocalPathCache
{
var cache = [CachedPath]()
var pathListIndex: Int
var curPathLength: Int
mutating func updateLocalPathCache(from pathList: [Path])
{
while pathListIndex < pathList.endIndex
{
let pathToCache =
pathList[pathListIndex][curPathLength...]
cache.append(pathToCache)
pathListIndex += 1
}
}
mutating func update(
mainCache: inout [Point: CachedPaths],
for src: Point)
{
if cache.count > 0 {
mainCache[src] = cache
}
}
}
private var localCaches = [LocalPathCache]()
private mutating func getLocalCache(
pathListIndex: Int,
curPathLength: Int) -> LocalPathCache
{
if var cache = localCaches.last
{
localCaches.removeLast()
cache.cache.removeAll(keepingCapacity: true)
cache.pathListIndex = pathListIndex
cache.curPathLength = curPathLength
return cache
}
return LocalPathCache(
pathListIndex: pathListIndex,
curPathLength: curPathLength
)
}
private mutating func freeLocalCache(_ cache: LocalPathCache) {
localCaches.append(cache)
}
private init(for graph: Graph)
{
self.graph = graph
self.pathList.reserveCapacity(graph.count)
self.currentPath.reserveCapacity(graph.count)
self.pathCache.reserveCapacity(graph.count)
}
static func pathList(for graph: Graph) -> [Path]
{
var pathFinder = Self(for: graph)
return pathFinder.makePathLists()
}
private mutating func makePathLists() -> [Path]
{
for src in graph.keys
{
for edge in graph[src]!
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
return pathList
}
private mutating func appendAllPaths()
{
assert(currentPath.count > 0, "currentPath must not be empty on entry")
guard let src = currentPath.last?.1 else { return }
if updatePathListFromCache(for: src) { return }
guard let edges = graph[src] else
{
if currentPath.count > 1 {
pathList.append(currentPath)
}
return
}
var localCache = getLocalCache(
pathListIndex: pathList.endIndex,
curPathLength: currentPath.endIndex
)
defer { freeLocalCache(localCache) }
for edge in edges
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
localCache.updateLocalPathCache(from: pathList)
}
localCache.update(mainCache: &pathCache, for: src)
}
mutating func updatePathListFromCache(for src: Point) -> Bool
{
if let cachedPaths = pathCache[src]
{
let curPathIndex = currentPath.endIndex
for path in cachedPaths
{
currentPath.append(contentsOf: path)
pathList.append(currentPath)
currentPath.removeSubrange(curPathIndex...)
}
return true
}
return false
}
}
Solution 3
Solutions 1 and 2 still use recursion. Recursion is elegant, and nice to think about, because you can express a problem as a slightly simpler problem plus a bit. But unless it's tail-recursive, compilers can't optimize it particularly well. So the solution to that problem is to use iteration instead of recursion.
Turning a recursive algorithm into an iterative one is not always so easy, and can result in ugly code. That is the definitely the case here. There might be a simpler iterative algorithm, but I don't know it. So I basically replaced recursion with a state machine + stack. I've used this technique before for recursive code the desperately needed to be faster, and it does work. The code is a total pain for a human to read and maintain, but compilers can optimize the hell out of it.
This version still uses the cached intermediate solutions from version 2.
struct PathFinderVersion3
{
private typealias CachedPath = Path.SubSequence
private typealias CachedPaths = Array<CachedPath>
private let graph: Graph
private var pathList = [Path]()
private var currentPath = Path()
private var pathCache = [Point: CachedPaths]()
private struct LocalPathCache
{
var cache = [CachedPath]()
var pathListIndex: Int
var curPathLength: Int
mutating func updateLocalPathCache(from pathList: [Path])
{
while pathListIndex < pathList.endIndex
{
let pathToCache =
pathList[pathListIndex][curPathLength...]
cache.append(pathToCache)
pathListIndex += 1
}
}
mutating func update(
mainCache: inout [Point: CachedPaths],
for src: Point)
{
if cache.count > 0 {
mainCache[src] = cache
}
}
}
private var localCaches = [LocalPathCache]()
private mutating func getLocalCache(
pathListIndex: Int,
curPathLength: Int) -> LocalPathCache
{
if var cache = localCaches.last
{
localCaches.removeLast()
cache.cache.removeAll(keepingCapacity: true)
cache.pathListIndex = pathListIndex
cache.curPathLength = curPathLength
return cache
}
return LocalPathCache(
pathListIndex: pathListIndex,
curPathLength: curPathLength
)
}
private mutating func freeLocalCache(_ cache: LocalPathCache) {
localCaches.append(cache)
}
private init(for graph: Graph)
{
self.graph = graph
self.pathList.reserveCapacity(graph.count)
self.currentPath.reserveCapacity(graph.count)
self.pathCache.reserveCapacity(graph.count)
}
static func pathList(for graph: Graph) -> [Path]
{
var pathFinder = Self(for: graph)
return pathFinder.makePathLists()
}
private mutating func makePathLists() -> [Path]
{
for src in graph.keys
{
for edge in graph[src]!
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
return pathList
}
struct Stack<T>
{
var storage: [T] = []
var isEmpty: Bool { storage.isEmpty }
var count: Int { storage.count }
init(capacity: Int) { storage.reserveCapacity(capacity) }
mutating func push(_ element: T) { storage.append(element) }
mutating func pop() -> T? { storage.popLast() }
}
private mutating func appendAllPaths()
{
assert(currentPath.count > 0, "currentPath must not be empty on entry")
enum State
{
case entry
case inLoopPart1
case inLoopPart2
case exit
}
var state: State = .entry
typealias StackElement =
(Point, Int, Line, [Line], LocalPathCache, State)
var stack = Stack<StackElement>(capacity: graph.count)
var src: Point! = nil
var edges: [Line]! = nil
var edgeIndex: Int = 0
var edge: Line! = nil
var localCache: LocalPathCache! = nil
outer: while true
{
switch state
{
case .entry:
if let s = currentPath.last?.1 {
src = s
}
else
{
state = .exit
continue outer
}
if updatePathListFromCache(for: src)
{
state = .exit
continue outer
}
if let e = graph[src] { edges = e }
else
{
if currentPath.count > 1 {
pathList.append(currentPath)
}
state = .exit
continue outer
}
localCache = getLocalCache(
pathListIndex: pathList.endIndex,
curPathLength: currentPath.endIndex
)
edgeIndex = edges.startIndex
state = .inLoopPart1
continue outer
case .inLoopPart1:
if edgeIndex < edges.endIndex
{
edge = edges[edgeIndex]
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
// Simulate function call
stack.push((src, edgeIndex, edge, edges, localCache, .inLoopPart2))
state = .entry
continue outer
}
localCache.update(mainCache: &pathCache, for: src)
state = .exit
case .inLoopPart2:
localCache.updateLocalPathCache(from: pathList)
edgeIndex += 1
state = .inLoopPart1 // Simulate goto top of inner loop
case .exit: // Simulate return
if let c = localCache { freeLocalCache(c) }
if let savedState = stack.pop()
{
(src, edgeIndex, edge, edges, localCache, state) = savedState
currentPath.removeLast()
}
else { break outer }
}
}
assert(stack.isEmpty)
}
mutating func updatePathListFromCache(for src: Point) -> Bool
{
if let cachedPaths = pathCache[src]
{
let curPathIndex = currentPath.endIndex
for path in cachedPaths
{
currentPath.append(contentsOf: path)
pathList.append(currentPath)
currentPath.removeSubrange(curPathIndex...)
}
return true
}
return false
}
}
Ummm... yeah, there it is in all its ugly glory. It works. It's fast. Good luck maintaining it.
The question is whether the speed is worth it when weighed against the readability issues and maintenance headaches, and that all depends on the application requirements. In my opinion that speed would have to be pretty darn important to put up with maintaining this version, and I'm normally fine with putting up with some ugliness to get speed in critical code. Somehow, this version, written in a language that doesn't even have a goto statement is nonetheless a poster-child for why goto is considered bad in the first place.
Solution 4 (Bonus)
Originally I just mentioned that you could parallelize it, but I didn't implement it. I decided that for completeness, a parallelization example really should be included.
I chose to parallelize solution 1, but all three of the previous solutions can be parallelized in exactly the same way. The changes that have to be made are to add the split method, and modify the static pathList(for:) method as well as the private makePathLists instance method. You also need a concurrent DispatchQueue. I create a global one for this example, but you can use an existing one. Don't use DispatchQueue.main for this, if you want your app to be responsive while processing.
Here's the code:
import Foundation
let dispatchQueue =
DispatchQueue(label: "PathFinder-\(UUID())",attributes: .concurrent)
struct PathFinderVersion4
{
private let graph: Graph
private var pathList = [Path]()
private var currentPath = Path()
private init(for graph: Graph)
{
self.graph = graph
self.pathList.reserveCapacity(graph.count)
self.currentPath.reserveCapacity(graph.count)
}
public static func pathList(for graph: Graph) -> [Path]
{
let concurrency = min(4, graph.count)
let pointGroups = split(.init(graph.keys), numberOfGroups: concurrency)
var pathLists = [[Path]](repeating: [], count: concurrency)
let waitSems = Array(
repeating: DispatchSemaphore(value: 0),
count: concurrency
)
for groupIndex in pointGroups.indices
{
dispatchQueue.async
{
defer { waitSems[groupIndex].signal() }
var pathFinder = Self(for: graph)
pathLists[groupIndex] =
pathFinder.makePathLists(for: pointGroups[groupIndex])
}
}
// Need to signal each semaphore after waiting or will crash on return.
// See Apple documentation for DispatchSemaphore
waitSems.forEach { $0.wait(); $0.signal() }
var result = [Path]()
result.reserveCapacity(pathLists.reduce(0) { $0 + $1.count })
pathLists.forEach { result.append(contentsOf: $0) }
return result
}
private static func split<Value>(
_ values: [Value],
numberOfGroups: Int) -> [[Value]]
{
var groups = [[Value]]()
groups.reserveCapacity(numberOfGroups)
let groupSize = values.count / numberOfGroups
+ (values.count % numberOfGroups == 0 ? 0 : 1)
var valueIndex = values.startIndex
while valueIndex < values.endIndex
{
var group = [Value]()
group.reserveCapacity(groupSize)
let valueEnd = min(valueIndex + groupSize, values.endIndex)
while valueIndex < valueEnd
{
group.append(values[valueIndex])
valueIndex += 1
}
groups.append(group)
}
return groups
}
private mutating func makePathLists(for points: [Point]) -> [Path]
{
for src in points
{
for edge in graph[src]!
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
return pathList
}
private mutating func appendAllPaths()
{
assert(currentPath.count > 0, "currentPath must not be empty on entry")
guard let src = currentPath.last?.1 else { return }
guard let edges = graph[src] else
{
if currentPath.count > 1 {
pathList.append(currentPath)
}
return
}
for edge in edges
{
assert(edge.0 == src, "sanity check failed")
currentPath.append(edge)
appendAllPaths()
currentPath.removeLast()
}
}
}
I am attempting to declare a linked list in Swift, with a finger type that is a reference to either a node, allowing to insert or remove beyond that node, or to the linked list itself, in which case inserting or removing at the top of the linked list.
I want to see if this can be made uniform down to the implementation, instead of having to special-case everything: Swift is object-oriented, after all.
I previously had a version which required forced casts, but again I'd like to see if this can be made to work without them (e.g. even if they never end up faulting they still imply runtime checks each time).
I currently have this code:
protocol ContainerNodeInterface: class {
associatedtype ContainedItem;
var contents: ContainedItem { get };
}
protocol ParentNodeInterface: class {
associatedtype LinkedItem: ContainerNodeInterface;
var next: LinkedItem? {get set};
}
class ProtoNode<Contents, NodeType: ParentNodeInterface>: ParentNodeInterface where NodeType.ContainedItem==Contents, NodeType.LinkedItem==NodeType { // not meant to be instantiated or directly referenced
typealias LinkedItem = NodeType;
var next: NodeType?;
init() {
next = nil;
}
final func insertThisAfterMe(_ node: NodeType) {
node.next = next;
next = .some(node);
}
final func removeNodeAfterMe() -> NodeType? {
guard let nextNode = next else {
return nil;
}
let result = nextNode;
next = result.next;
result.next = nil;
return nextNode;
}
}
class Node<Contents>: ProtoNode<Contents, Node<Contents>>, ContainerNodeInterface {
typealias ContainedItem = Contents;
typealias NextItem = Node<Contents>;
var contents: Contents;
init(withContents: Contents) {
contents = withContents;
super.init();
}
}
typealias ParentNode<Contents> = ProtoNode<Contents, Node<Contents>>;
But the Swift compiler, via Xcode, is complaining that Type 'Node<Contents>' does not conform to protocol 'ParentNodeInterface'. This makes no sense! And if I add explicit conformance to ParentNodeInterface to Node, then I get simultaneously that error and one of redundant conformance to the same protocol.
What is missing here?
Xcode Version 10.2 (10E125), Swift 5
I resolved it by splitting ProtoNode into an initial declaration and an extension:
protocol ContainerNodeInterface: class {
associatedtype ContainedItem;
var contents: ContainedItem { get };
}
protocol ParentNodeInterface: class {
associatedtype LinkedItem: ContainerNodeInterface;
var next: LinkedItem? {get set};
}
class ProtoNode<Contents, NodeType: ContainerNodeInterface>: ParentNodeInterface where NodeType.ContainedItem==Contents { // not meant to be instantiated or directly referenced
typealias LinkedItem = NodeType;
var next: NodeType?;
init() {
next = nil;
}
}
extension ProtoNode where NodeType: ParentNodeInterface, NodeType.LinkedItem==NodeType
{
final func insertThisAfterMe(_ node: NodeType) {
node.next = next;
next = .some(node);
}
final func removeNodeAfterMe() -> NodeType? {
guard let nextNode = next else {
return nil;
}
let result = nextNode;
next = result.next;
result.next = nil;
return nextNode;
}
}
class Node<Contents>: ProtoNode<Contents, Node<Contents>>, ContainerNodeInterface {
typealias ContainedItem = Contents;
typealias NextItem = Node<Contents>;
var contents: Contents;
init(withContents: Contents) {
contents = withContents;
super.init();
}
}
typealias ParentNode<Contents> = ProtoNode<Contents, Node<Contents>>;
I figure it helps the compiler break the dependency loop, where it has to determine whether Node, as a generic parameter, conforms to the protocol before it can determine the declaration is valid and consider the declared type, i.e. Node, as conforming to the protocol, but still it feels a bit silly for me to have to make this seemingly pointless extension declaration.
At the very least, the compiler could be slightly more helpful…
First, I would start with a simple linked-list Node type:
final class Node<Value> {
let value: Value
var next: Node<Value>?
init(_ value: Value) {
self.value = value
}
func insert(_ node: Node<Value>) {
node.next = next
next = node
}
func removeNext() -> Node<Value>? {
guard let removedNode = next else { return nil }
next = removedNode.next
removedNode.next = nil
return removedNode
}
}
Then, you can add the concept that you describe: a pointer to "either a node...or to the linked list itself." When you see "or" in a description, that implies a sum type, which in Swift is an enum, either a pointer to the head of a (possibly empty) list, or a pointer to a node. Each has slightly different behaviors, which you manage with switch.
enum NodePointer<Value> {
case head(Node<Value>?)
case node(Node<Value>)
mutating func insert(_ node: Node<Value>) {
switch self {
case .head(let n):
self = .head(node)
node.next = n
case .node(let n):
n.insert(node)
}
}
mutating func removeNext() -> Node<Value>? {
switch self {
case .head(let n):
self = .head(n?.next)
return n
case .node(let n):
return n.removeNext()
}
}
var pointee: Node<Value>? {
switch self {
case .head(let n): return n
case .node(let n): return n
}
}
}
With that you would have an interface like:
var list = Node(1)
list.insert(Node(2))
var ptr = NodePointer.head(list)
ptr.insert(Node(1))
ptr.pointee?.next?.next?.value // 2
Note that the specific problem you ran into (that the compiler couldn't work out the conformance) I believe is a compiler bug, though I also believe it's one that's fixed on master currently. I haven't tested that out though. But I don't believe the protocol-based approach is correct for this problem.
Just messing around with the language thinking of how I want to structure some UserDefaults that automatically generate keys based on the hierarchy. That got me wondering... Is it possible to simultaneously define, and instantiate a type, like this?
let myUserSettings = {
let formatting = {
var lastUsedFormat:String
}
}
let lastUsedFormat = myUserSettings.formatting.lastUsedFormat
Note: I can't use statics because I specifically need instancing so nested structs/classes with static members will not work for my case.
Here's the closest thing I could come up with, but I hate that I have to create initializers to set the members. I'm hoping for something a little less verbose.
class DefaultsScope {
init(_ userDefaults:UserDefaults){
self.userDefaults = userDefaults
}
let userDefaults:UserDefaults
func keyForSelf(property:String = #function) -> String {
return "\(String(reflecting: self)).\(property)"
}
}
let sharedDefaults = SharedDefaults(UserDefaults(suiteName: "A")!)
class SharedDefaults : DefaultsScope {
override init(_ userDefaults:UserDefaults){
formatting = Formatting(userDefaults)
misc = Misc(userDefaults)
super.init(userDefaults)
}
let formatting:Formatting
class Formatting:DefaultsScope {
let maxLastUsedFormats = 5
fileprivate(set) var lastUsedFormats:[String]{
get { return userDefaults.stringArray(forKey:keyForSelf()) ?? [] }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
func appendFormat(_ format:String) -> [String] {
var updatedListOfFormats = Array<String>(lastUsedFormats.suffix(maxLastUsedFormats - 1))
updatedListOfFormats.append(format)
lastUsedFormats = updatedListOfFormats
return updatedListOfFormats
}
}
let misc:Misc
class Misc:DefaultsScope {
var someBool:Bool{
get { return userDefaults.bool(forKey:keyForSelf()) }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
}
}
So is there a simpler way?
Disclaimer: this is, probably, just an abstract solution that should not be used in real life :)
enum x {
enum y {
static func success() {
print("Success")
}
}
}
x.y.success()
Update: Sorry, folks, I can't stop experimenting. This one looks pretty awful :)
let x2= [
"y2": [
"success": {
print("Success")
}
]
]
x2["y2"]?["success"]?()
Update 2: One more try, this time with tuples. And since tuples must have at least two values, I had to add some dummies in there. Also, tuples cannot have mutating functions.
let x3 = (
y3: (
success: {
print("Success")
},
failure: {
print("Failure")
}
),
z3: 0
)
x3.y3.success()
How about you try nesting some swift structs?
struct x {
struct y {
static func success() {
print("success")
}
}
}
x.y.success()
You cannot have that kind of structure but you cant access y from inside x, since y is only visible inside the scope of x and so is success inside the scope of y. There is no way that you can access them from outside
One other alternative is to have higher order function like so, which return closure which is callable.
let x = {
{
{
print("Success")
}
}
}
let y = x()
let success = y()
success()
or
x()()()
The real world usage of higher order function for userdefaults could be something like this,
typealias StringType = (String) -> ((String) -> Void)
typealias IntType = (String) -> ((Int) -> Void)
typealias BoolType = (String) -> ((Bool) -> Void)
typealias StringValue = (String) -> String?
typealias IntValue = (String) -> Int?
typealias BoolValue = (String) -> Bool?
func userDefaults<T>(_ defaults: UserDefaults) -> (String) -> ((T) -> Void) {
return { key in
return { value in
defaults.setValue(value, forKey: key)
}
}
}
func getDefaultsValue<T>(_ defaults: UserDefaults) -> (String) -> T? {
return { key in
return defaults.value(forKey: key) as? T
}
}
let setStringDefaults: StringType = userDefaults(.standard)
setStringDefaults("Name")("Jack Jones")
setStringDefaults("Address")("Australia")
let setIntDefaults: IntType = userDefaults(.standard)
setIntDefaults("Age")(35)
setIntDefaults("Salary")(2000)
let setBoolDefaults: BoolType = userDefaults(.standard)
setBoolDefaults("Married")(false)
setBoolDefaults("Employed")(true)
let getStringValue: StringValue = getDefaultsValue(.standard)
let name = getStringValue("Name")
let address = getStringValue("Address")
let getIntValue: IntValue = getDefaultsValue(.standard)
let age = getIntValue("Age")
let salary = getIntValue("Salary")
let getBoolValue: BoolValue = getDefaultsValue(.standard)
let married = getBoolValue("Married")
let employed = getBoolValue("Employed")
I am not sure if you like the pattern, but it has some good use cases as you can see from below, setStringDefaults you can set strings value to string key and all of them are typesafe.
You can extend this for your use case. But, you could use struct as well and use imperative code, which could be easier to understand. I see beauty in this as well.
Ok, I think I've figured it out. This first class can go in some common library that you use for all your apps.
class SettingsScopeBase {
private init(){}
static func getKey(setting:String = #function) -> String {
return "\(String(reflecting:self)).\(setting)"
}
}
The next part is a pair of classes:
The 'Scoping' class where you define which user defaults instance to use (along with anything else you may want to specify for this particular settings instance)
The actual hierarchy that defines your settings
Here's the first. I'm setting this up for my shared settings between my application and it's extension:
class SharedSettingsScope : SettingsScopeBase{
static let defaults = UserDefaults(suiteName: "group.com.myco.myappgroup")!
}
And finally, here's how you 'set up' your hierarchy as well as how you implement the properties' bodies.
class SharedSettings:SharedSettingsScope{
class Formatting:SharedSettingsScope{
static var groupsOnWhitespaceOnlyLines:Bool{
get { return defaults.bool(forKey: getKey()) }
set { defaults.set(newValue, forKey: getKey()) }
}
}
}
And here's how you use them...
let x = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// x = false
SharedSettings.Formatting.groupsOnWhitespaceOnlyLines = true
let y = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// y = true
I'm going to see if I can refine/optimize it a little more, but this is pretty close to where I want to be. No hard-coded strings, keys defined by the hierarchy where they're used, and only setting the specific UserDefaults instance in one place.