Sunday, June 15, 2014

Sequences aren't first-class sequences

Here's some simple code:
    var a2d: Int[][] = [[], []]
    for a1d in a2d { a1d.append(1) }
You might think this would change a2d to [[1], [1]], just as the equivalent code in Python, C++, or almost any other language would. You'd be wrong. It does this:
    :63:18: error: immutable value of type 'Array' only has mutating members named 'append'
Apparently iterating over a Sequence gives you immutable values. You can try to write your own Sequence and Generator that doesn't work that way, and it'll work that way anyway, just as the built-in ones do.

So, what's the workaround? For arrays, of course, you can always write code like this:
    for i in 0..a2d.count { a2d[i].append(1) }
But for a general-purpose Sequence, there's just nothing you can do.

Well, I suppose you could give up on mutability and write everything in Swift immutably. In Python or Haskell, that tends to be briefer and often faster. What about Swift? Well, here's the code to build a new 2D array that's a copy of the old one but with 1 appended to every row:
    let tmp: Int[][]
    for a1d in a2d {
        var row = []
        for val in a1d {
            row = row + [val] // don't use += here!
        }
        row = row + [1]
        tmp = tmp + [row]
    }
So, only 9x as much code. And from a quick test, it only takes 23x longer to run. Great!

1 comment:

  1. Another place I ran into this: I have an array of Generators, and I want an array of their first values. You can't do it with map, or a for gen in gens loop; you need a for i in 0..gens.count loop. Sigh…

    ReplyDelete