Jonathan
Jonathan

Reputation: 5884

How does this "common idiom" actually work?

Looking at documentation for golang's 2D slices and cannot understand the syntax used in the last example:

func main() {
    XSize := 5
    YSize := 5

    // Allocate the top-level slice, the same as before.
    picture := make([][]uint8, YSize) // One row per unit of y.

    // Allocate one large slice to hold all the pixels.
    pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.

    // Loop over the rows, slicing each row from the front of the remaining pixe ls slice.
    for i := range picture {
        picture[i], pixels = pixels[:XSize], pixels[XSize:]
    }
}

I found the change request where this was added to the docs and the change author had this normal / easy to understand code:

// Loop over the rows, slicing each row.
for i := range picture {
     picture[i] = pixels[i*XSize:(i+1)*XSize]

However, there is the following comment:

fine. another common idiom is to avoid the math:

picture[i], pixels = pixels[:XSize], pixels[XSize:]

My question is how does the above achieve the same as the !"avoid the math" method? Some docs on what is going on would be great.

Upvotes: 1

Views: 82

Answers (2)

icza
icza

Reputation: 418635

This:

picture[i], pixels = pixels[:XSize], pixels[XSize:]

Is a tuple assignment. It assigns a value to picture[i] and a value to pixels. The values assigned in order are pixels[:XSize] and pixels[XSize:].

The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.

What happens here is that when the loop starts (i = 0), the first element of picture (the first row) is assigned with a slice value being the first XSize elements in pixels, and the pixels slice is resliced so its first element will be the XSizeth element +1.

So in the next iteration picture[i] will be the 2nd element in the picture (the 2nd row), and again, the first XSize elements from pixels will be set to it as a slice. But since in the previous iteration we resliced pixels, in each iteration its first XSize elements will be the subsequent rows.

Upvotes: 5

levif
levif

Reputation: 2176

This tuple assignment example can be rewritten like:

for i := range picture {
    picture[i]= pixels[:XSize]
    pixels = pixels[XSize:]
}

It's now easier to see that picture is this first XSize items of pixels.

And that pixels is modified at each loop and drops its first XSize items.

Upvotes: 2

Related Questions