Jack Richards
Jack Richards

Reputation: 133

How to break up expressions involve bitwise shift operators? Swift

The code below is generating an error and fails to run as the expression was too 'complex'.

for row in 0...NumRows {
        for column in 0...NumColumns {
            let topLeft = (column > 0) && (row < NumRows)
                && level.tileAt(column: column - 1, row: row) != nil
            let bottomLeft = (column > 0) && (row > 0)
                && level.tileAt(column: column - 1, row: row - 1) != nil
            let topRight = (column < NumColumns) && (row < NumRows)
                && level.tileAt(column: column, row: row) != nil
            let bottomRight = (column < NumColumns) && (row > 0)
                && level.tileAt(column: column, row: row - 1) != nil

            let value = Int(topLeft.hashValue) | Int(topRight.hashValue) << 1 | Int(bottomLeft.hashValue) << 2 | Int(bottomRight.hashValue) << 3

            if value != 0 && value != 6 && value != 9 {

                let name = String(format: "Tile_%ld", value)
                let tileNode = SKSpriteNode(imageNamed: name)
                tileNode.size = CGSize(width: TileWidth, height: TileHeight)
                var point = pointFor(column: column, row: row)
                point.x -= TileWidth/2
                point.y -= TileHeight/2
                tileNode.position = point
                tilesLayer.addChild(tileNode)

            }
        }
    }

Specifically this line:

let value = Int(topLeft.hashValue) | Int(topRight.hashValue) << 1 | Int(bottomLeft.hashValue) << 2 | Int(bottomRight.hashValue) << 3

The error is as follows:

Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions

How would you break up the expression for value in such a way that the code following it keeps the same answer?

Upvotes: 0

Views: 122

Answers (1)

Martin R
Martin R

Reputation: 539705

hashValue is already an Int, so the Int(...) constructors are not needed. This compiles in Xcode 9:

let value = topLeft.hashValue | (topRight.hashValue << 1) | (bottomLeft.hashValue << 2) | (bottomRight.hashValue << 3)

But that is a bad idea! It relies on the hash value of booleans having specific values:

false.hashValue == 0
true.hashValue == 1

which is nowhere guaranteed. (The only guarantee is that identical elements have the same hash value. One must not even assume that hash values are equal across different executions of your program, see Hashable).

A better solution would be

let value = (topLeft ? 1 : 0) + (topRight ? 2 : 0) + (bottomLeft ? 4 : 0) + (bottomRight ? 8 : 0)

Alternatively, use a switch-statement. Example:

switch (topLeft, topRight, bottomLeft, bottomRight) {
case (false, false, false, false):
    // ... all false ...
case (false, true, true, false):
    // ... topRight and bottomLeft are true ...
case (true, false, false, true):
    // ... topLeft and bottomRight are true ...
default:
    // ... all other cases ...
}

Upvotes: 2

Related Questions