Swift on array.sort - Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions - swift

I am downgrading Swift code from Xcode 8.3.1 to Xcode 7.3.1.
The Swift compiler of Xcode 7.3.1 raises
Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
while pointing on line zeroParameterAndPaths.sort {. The code was ok in Xcode 8.3.1.
What's wrong and how to fix it?
class NewConnectingSegmentZeroParameterAndPath {
let step : Int; // 0 = main, 1 = first outline, 2 = second outline
let parameter : CGFloat;
init(step: Int, parameter: CGFloat) {
self.step = step;
self.parameter = parameter;
}
}
var zeroParameterAndPaths : [NewConnectingSegmentZeroParameterAndPath] = [];
// ... some zeroParameterAndPaths .appendContentsOf calls
zeroParameterAndPaths.sort {
return $0.parameter < $1.parameter
|| ($0.parameter == $1.parameter
&& ($0.step == 1 || ($0.step == 0 && $1.step == 2))
)
};

You have two choices. One is simply to do what the error message suggests, i.e. pulling the complex bool apart into separate pieces:
zeroParameterAndPaths.sort {
let bless = ($0.parameter < $1.parameter)
let beq = ($0.parameter == $1.parameter)
let band = ($0.step == 0 && $1.step == 2)
let bor = ($0.step == 1 || band)
let beqandbor = (beq && bor)
return (bless || beqandbor)
};
The other is to provide an explicit in line giving the param types and result type:
zeroParameterAndPaths.sort {
(a:NewConnectingSegmentZeroParameterAndPath, b:NewConnectingSegmentZeroParameterAndPath) -> Bool in
return a.parameter < b.parameter
|| (a.parameter == b.parameter
&& (a.step == 1 || (a.step == 0 && b.step == 2))
)
};

You could also make your class a little bit more helpful and make it implement the condition. The compiler is much less likely to get confused in a function body than in a closure:
class NewConnectingSegmentZeroParameterAndPath {
let step : Int; // 0 = main, 1 = first outline, 2 = second outline
let parameter : CGFloat;
init(step: Int, parameter: CGFloat) {
self.step = step;
self.parameter = parameter;
}
func orderedBefore(_ other: NewConnectingSegmentZeroParameterAndPath) -> Bool
{
return parameter < other.parameter
|| parameter == other.parameter
&& (step == 1 || step == 0 && other.step == 2)
}
}
var zeroParameterAndPaths : [NewConnectingSegmentZeroParameterAndPath] = [];
// ... some zeroParameterAndPaths .appendContentsOf calls
zeroParameterAndPaths.sort { $0.orderedBefore($1) }

Apart from the issue of the type inference engine not being able to quickly resolve such complex bool expressions, such expressions are really hard to follow. I suggest you break it down into something simpler, like so:
zeroParameterAndPaths.sort {
if $0.parameter != $1.parameter { return $0.parameter < $1.parameter ]
if $0.step == 1 { return true }
if $0.step == 0 && $1.step == 2 { return true }
return false
};
There's my attempt at it. I'm not even sure if it's correct, the original expression is pretty hard to follow.

Related

JS timeout causes eval exception

For some reason, one of my JS files is triggering an unsafe-eval Content Security Policy violation on my site. I thought this odd because there is no eval() anywhere in the file. The error happens on the following line:
setTimeout(callSpecific(), (lengthMF * (number.length + 2)));
The only thing I can see here is the arithmetic on the RHS that sets the timeout value. So, I tried:
setTimeout(callSpecific(), (parseInt(lengthMF) * (parseInt(number.length) + 2)));
Same thing. The variables themselves are not even strings - they are defined as:
var lengthMF = 150;
var number = ""; // yes, this is a string but number.length is not!
Why is this triggering a CSP violation? I have other setTimeout()s on the page and this seems to be the only problematic one. The weird thing is replacing the arithmetic expression temporarily with a constant (e.g. 50) does not cause the issue to go away.
If it's necessary, callSpecific() looks something like this:
function callSpecific() {
if (number == 0) {
operatorRing();
} else if (number.length == 2 && number.charAt(1) == 0) {
playReorder();
} else if (number.length == 3) {
//
} else if (number.length <7 || number.length > 11) {
//
} else if (number.length == 11 && (number.charAt(4) == 1 || number.charAt(4) == 0)) {
//
}
}

Change value of variable by reference

Hi I have 4 variables and I want to refer to one of them depending on some conditions, my current code looks as follows:
switch color {
case .Azul:
botonACambiar.tintColor = colores.salidaAzul
if backTees == true {
if option == 1{self.colorHcpBackTees1 = .Azul} else{self.colorHcpBackTees2 = .Azul}
} else {
if option == 1{self.colorHcpFrontTees1 = .Azul} else{self.colorHcpFrontTees2 = .Azul}
}
...
...
case . Cafe:
botonACambiar.tintColor = colores.salidaMarron
if backTees == true {
if option == 1{self.colorHcpBackTees1 = .Cafe} else{self.colorHcpBackTees2 = .Cafe}
} else {
if option == 1{self.colorHcpFrontTees1 = .Cafe} else{self.colorHcpFrontTees2 = .Cafe}
}
}
I have around 8 options, and as you can see it gets kind of messy.
So far I only have 4 different variables that i might manipulate:
Option 1 & 2 for FrontTees & option 1 & 2 for BackTees, but in the future the app might support more options and more colors for front and back tees.
This 4 values are saved in the app and because of this I need to keep track of them individually, thus I have the four variables:
var colorHcpBackTees1: ColoresDeSalidas = .Negra
var colorHcpBackTees2: ColoresDeSalidas = .Blanca
var colorHcpFrontTees1: ColoresDeSalidas = .Roja
var colorHcpFrontTees2: ColoresDeSalidas = .Blanca
Each with their default values, it would be nice to have a way of initializing a reference variable in such a way that I could do something like:
var choice: ColoresDeSalidas{
if backTees && option == 1 { return self.colorHcpBackTees1}
if backTees && option == 2 { return self.colorHcpBackTees2}
if !backTees && option == 1 { return self.colorHcpFrontTees1}
if !backTees && option == 2 { return self.colorHcpFrontTees2}
}
And simply use the variable choice to manipulate the value of the right variable
You can accomplish this using a ReferenceWritableKeyPath.
What is a ReferenceWritableKeyPath?
Think of it as a precise description of a property in a class or struct. A real world example would be The Empire State Building instead of the address which would be 20 W 34th St, New York, NY 10001. You could tell either one to a cab driver and she could take you there.
A ReferenceWritableKeyPath is a generic type. You need to specify the class or struct name and the type of the variable you will be accessing. So your choice variable would be a ReferenceWritableKeyPath<YourClass,ColoresDeSalidas>.
Here is a standalone example:
enum ColoresDeSalidas : String {
case Negra
case Blanca
case Roja
case Azul
case Verde
}
class Foo {
var backTees = false
var option = 1
var colorHcpBackTees1: ColoresDeSalidas = .Negra
var colorHcpBackTees2: ColoresDeSalidas = .Blanca
var colorHcpFrontTees1: ColoresDeSalidas = .Roja
var colorHcpFrontTees2: ColoresDeSalidas = .Blanca
var choice: ReferenceWritableKeyPath<Foo,ColoresDeSalidas> {
if backTees && option == 1 { return \.colorHcpBackTees1 }
if backTees && option == 2 { return \.colorHcpBackTees2 }
if !backTees && option == 1 { return \.colorHcpFrontTees1 }
if !backTees && option == 2 { return \.colorHcpFrontTees2 }
fatalError("We were supposed to return a keyPath for choice")
}
func test() {
backTees = true
option = 2
print("Before: \(self.colorHcpBackTees2)")
// Now update the correct property using the choice KeyPath
self[keyPath: choice] = .Azul
print("After: \(self.colorHcpBackTees2)")
backTees = false
option = 1
// Assign it to another variable, just to show you can
let choiceFront1 = choice
option = 2
// choiceFront1 still refers to !backTees and option 1
// even though option and choice have changed
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")
colorHcpFrontTees1 = .Verde
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")
}
}
Run the test:
Foo().test()
Output:
Before: Blanca
After: Azul
colorHcpFrontTees1 = Roja
colorHcpFrontTees1 = Verde
To clean the code you could wrap the logic for initialising the properties within the enum itself.
extension ColoresDeSalidas {
init?(isBackTee: Bool, option: Int) {
switch (isBackTee, option) {
case (true, 1): self = .colorHcpBackTees1
case (true, 2(: self = .colorHcpBackTees2
case (false, 1): self = .colorHcpFrontTees1
case (false, 2): self = .colorHcpFrontTees2
default: return nil //or assign a default and make non-failable
}
And then you can initialise / update with:
let myVar = ColoresDeSalidas(isBackTee: true, option: 1)

How to compare two string indexes in swift 4

I'm trying to find whether the character "S" or "C" appears in a string. At least one will be in the string but not necessarily both.
let S = codeZ.characters.index(of: "S")
let C = codeZ.characters.index(of: "C")
if (C == nil) || S < C {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
else if S == nil || (C < S) {
nextView.sequential_flag = false
nextView.continuous_flag = true
}
I'm getting the error : Binary operator '<' cannot be applied to two 'String.CharacterView.Index?' (aka 'Optional') operands
In my experience with swift this usually means something else if wrong.
Also I've tried changing the if statements to this below.
if (C == nil) || S?.encodedOffset < C?.encodedOffset {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
And I got the error : Binary operator '<' cannot be applied to two 'Int?' operands.
Any help on how to do this is much appreciated, thank you.
You should check if S is nil or not and provide a fallback value to C. Then you can compare the two non-optional values.
if let S = S, S.encodedOffset < (C?.encodedOffset ?? Int.max) {
nextView.sequential_flag = true
nextView.continuous_flag = false
}

swift 3 variable used before begin initialized

I have an issue with my n variable. I cannot use n in for loop. Why? n was initialized before for loop. Please, help.
import Foundation
var n: Int
var t: Int
while(true){
var tt = readLine()
t = Int(tt!)!
if (t==0){
break
}
else if ( t < 0){
n = t*(-1)
}
else if(t > 0){
n = t
}
var arr : [[String]] = []
for i in 0..<n*2{
for y in 0..<n*2{
arr[i][y] = "."
}
}
}
A variable may be declared and not immediately initialized, as long as initialization is guaranteed before first use
The error is more subtle than at first glance. You may actually declare a property without initializing it, as long as all program flows leading to its first use ascertain initialization of it.
The issue is with the if, else if and else if block:
var n: Int // declaration
// ...
if (t == 0) {
break
}
else if (t < 0) {
n = t*(-1)
}
else if (t > 0){
n = t
}
// first use
for i in 0..<n*2 { /* ... */ }
Swift cannot not infer that this block is in fact exhaustive, and believes that there is a possibility that none of the above if statements holds, which, in the eyes of the compiler, would lead to the following program state:
program flow has not been broken (break)
and n has not been instantiated
As humans, however, we know that the if - else if - else if block above is indeed exhaustive, and can help the compiler out by simply changing the last if else statement to a simple else statement.
if (t == 0) {
break
}
else if (t < 0) {
n = t*(-1)
}
// if none of the above, t > 0
else {
n = t
}
On another note, the nested array access of non-existing array elements, arr[i][y] = "." will lead to a runtime exception, but this is another issue. In its current form, it looks as if the intent with the nested loops could be replaced with a nested array instantiation:
var arr = [[String]](repeating: [String](repeating: ".", count: 2*n), count: 2*n)
or,
var arr = (0..<2*n).map { _ in [String](repeating: ".", count: 2*n) }
The variable n is only declared, not initialized.
To initialize the variables:
var n: Int = 0
var t: Int = 0

Recursive Pathfinding in Swift - find longest path

(Sorry for the cross-post, but I put this up on GameDev.Stackexchange and it's not getting much traffic, so I thought I'd try here as well.)
I'm programming a simple tile-based puzzle game, and I've gotten stuck trying to work out a pathfinding algorithm.
Here's how the game is set out:
The game board is (arbitrarily) 8 tiles wide by 8 tiles tall.
Each tile can be one of four types (shown below as red, green, blue and yellow)
Additionally, a tile could be a reactor (the starting point of a path - this will become clear later on)
The board would look something like this:
(The reactors are the circles; the other tiles have no special properties.)
What I need to do is: starting from a reactor, trace the longest path along adjoining tiles of the same color as the reactor. Something like this:
The blue reactor is simple(ish) as its path doesn't branch. However, as you can see from the green reactor's start position, its path can branch two ways at the start (up or down), and take a detour midway through.
The path I'm looking for is the longest one, so that's the one that's highlighted in the screengrab (the first path only crosses two tiles, and the detour midway results in a sorter path).
When certain conditions have been fulfilled, the reactor will cause all the tiles in the longest path (where the arrows cross in the diagram) to disappear and be replaced with new ones. All other tiles will remain in place, including the extraneous green tiles adjacent to the green reactor's path.
The tiles are stored in an approximation of a 2D array (Swift doesn't have a robust native implementation of that yet, so I'm using the one described in this tutorial). They're retrieved using tile[column, row].
With some help from a friend, I've written a recursive function that should return the longest path. It's looping through correctly, but it's not pruning shorter branches from the longestPath array (for example, the longest path would include the 2-tile branch below the reactor, as well as the single-tile detour at the top of the arch).
Can anyone see where I'm going wrong in this code?
Here's the recursive function:
func pathfinder(startingTile: Tile, pathToThisPoint: Chain, var iteration: Int? = 1) -> Chain
{
var longestPath: Chain? = nil
var availableTiles = getNeighbouringTiles(startingTile)
for var nextTile = 0; nextTile < availableTiles.count; nextTile++
{
let column = availableTiles[nextTile].column
let row = availableTiles[nextTile].row
if tiles[column, row]!.tileType == startingTile.tileType && (tiles[column, row]!.isReactor == false || startingTile.isReactor)
{
// if we haven't been here before
if !pathToThisPoint.tiles.contains(tiles[column, row]!)
{
print(iteration)
iteration = iteration! + 1
// add this tile to the pathtothispoint
// go to the next unexplored tile (recurse this function)
pathToThisPointaddTile(tiles[column, row]!)
let tempPath = pathfinder(tiles[column, row]!, pathToThisPoint: pathToThisPoint)
// if the resulting path is longer...
if tempPath.length > longestPath.length
{
// then tempPath is now the longest path
for var i:Int = 0; i < tempPath.length; i++
{
let tile = Tile(column: pathToThisPoint.tiles[i].column, row: pathToThisPoint.tiles[i].row, tileType: pathToThisPoint.tiles[i].tileType)
longestPath?.addTile(tile)
}
}
}
}
if longestPath != nil
{
return longestPath!
}
else
{
return pathToThisPoint
}
}
It's dependent on the getNeighboringTiles function (shown below) that returns an array of valid tiles of the same type, excluding reactors:
func getNeighbouringTiles(tile: Tile, previousTile: Tile? = nil) -> Array<Tile>
{
var validNeighbouringTiles = Array<Tile>()
var neighbourTile: Tile
// check top, right, bottom, left
if tile.row < NumRows - 1
{
neighbourTile = tiles[tile.column, tile.row + 1]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.column < NumColumns - 1
{
neighbourTile = tiles[tile.column + 1, tile.row]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.row > 0
{
neighbourTile = tiles[tile.column, tile.row - 1]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.column > 0
{
neighbourTile = tiles[tile.column - 1, tile.row]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
// if we get this far, they have no neighbour
return validNeighbouringTiles
}
The Tile class looks like this (methods omitted for brevity):
class Tile: CustomStringConvertible, Hashable
{
var column:Int
var row:Int
var tileType: TileType // enum, 1 - 4, mapping to colors
var isReactor: Bool = false
// if the tile is a reactor, we can store is longest available path here
var reactorPath: Chain! = Chain()
}
And finally, the chain class looks like this (again, methods omitted for brevity):
class Chain {
// The tiles that are part of this chain.
var tiles = [Tile]()
func addTile(tile: Tile) {
tiles.append(tile)
}
func firstTile() -> Tile {
return tiles[0]
}
func lastTile() -> Tile {
return tiles[tiles.count - 1]
}
var length: Int {
return tiles.count
}
}
----------------------- EDIT : REPLACEMENT PATHFINDER ------------------------
I've attempted to convert User2464424's code to Swift. Here's what I've got:
func calculatePathsFromReactor(reactor: Tile) -> Chain?
{
func countDirections(neighbours: [Bool]) -> Int
{
var count: Int = 0
for var i:Int = 0; i < neighbours.count; i++
{
if neighbours[i] == true
{
count++
}
}
return count
}
var longestChain: Chain? = nil
longestChain = Chain()
var temp: Chain = Chain()
var lastBranch: Tile = reactor
var lastMove: Int? = reactor.neighbours.indexOf(true)
func looper(var currentTile: Tile)
{
if currentTile != reactor
{
if countDirections(currentTile.neighbours) > 2 //is branch
{
lastBranch = currentTile
}
if countDirections(currentTile.neighbours) == 1 //is endpoint
{
lastBranch.neighbours[lastMove!] = false // block move out of the last branch found
if longestChain.length < temp.length
{
longestChain = temp
}
currentTile = reactor // return to reactor and redo
lastVisitedTile = reactor
temp = Chain() //reset to empty array
lastBranch = reactor
lastMove = reactor.neighbours.indexOf(true)
looper(currentTile)
}
}
//let tempTile: Tile = Tile(column: currentTile.column, row: currentTile.row, tileType: currentTile.tileType, isReactor: currentTile.isReactor, movesRemaining: currentTile.movesRemaining)
//tempTile.neighbours = currentTile.neighbours
if currentTile.neighbours[0] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 0
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column, currentTile.row + 1]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[1] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 1
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column + 1, currentTile.row]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[2] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 2
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column, currentTile.row - 1]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[3] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 3
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column - 1, currentTile.row]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
}
// trigger the function for the reactor tile
looper(reactor)
return longestChain
}
(The neighbours property is a struct containing four named variables: above, right, below and left, each initialised to false and then set to true by a function that runs immediately before the pathfinder.)
I'm finding a couple of issues now. The code loops as it should, but stops at the top of the arch, under the single-tile detour - the path that's returned is only 4 tiles long (including the reactor).
The other problem I'm having - which I'll worry about when the correct paths are being returned - is that I'm getting a memory access error when shifting the tiles in the third column down by one. I think it's getting confused when there's a block of tiles (2x2 or higher) rather than a path that's only ever a single tile wide.
I can't fix your code, but i have an idea for a system that doesn't require recursion.
You can try doing all possible paths from a reactor and block paths that you already traversed by being aware of the moves you have done when encountering a branch.
In the tile class, add another array of 4 integers initialized to 0 (called "dir" for example).
Pseudocode.
Do a preprocess loop first:
foreach tiles:
if tileHasNORTHNeighbor: tile.dir[0] = 1;
if tileHasEASTNeighbor: tile.dir[1] = 1;
if tileHasSOUTHNeighbor: tile.dir[2] = 1;
if tileHasWESTNeighbor: tile.dir[3] = 1;
Then do:
tile currentTile = reactor;
array longest;
array temp;
tile lastBranch = reactor;
int lastMove = any key of "reactor.dir" with "1" as value;
function int countOnes(array dir):
int count = 0;
int t;
for (t=0;t<4;t++):
if dir[t]==1:
count++;
return count;
:start
if currentTile != reactor:
if countOnes(currentTile.dir) > 2: //is branch
lastBranch = currentTile;
if countOnes(currentTile.dir) == 1: //is endpoint
lastBranch.dir[lastMove] = 0; // block move out of the last branch found
if longest.length < temp.length:
longest = temp;
currentTile = reactor; // return to reactor and redo
array temp = []; //reset to empty array
lastBranch = reactor;
lastMove = next "reactor.dir" key with "1" as value;
goto start;
if currentTile.dir[0] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 0;
currentTile = getTileAtNORTH; //must avoid going backwards
goto start;
if currentTile.dir[1] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 1;
currentTile = getTileAtEAST; //must avoid going backwards
goto start;
if currentTile.dir[2] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 2;
currentTile = getTileAtSOUTH; //must avoid going backwards
goto start;
if currentTile.dir[3] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 3;
currentTile = getTileAtWEST; //must avoid going backwards
goto start;
You could use the BFS Algorithm and easily modify it to give you the longest path.
You've got a implementation example here. Or you've got at least SwiftStructures and SwiftGraph github repositories with graph and search algorithms already implemented in swift.