← Back to context

Comment by DonHopkins

8 years ago

I like to break expressions across multiple lines, and line up any patterns that are repeated, so your eye can easily scan up and down and obviously see what's the same, and what's different.

For example, instead of:

    var length = Math.sqrt((x * x) + (y * y));

You can go:

    var length = Math.sqrt((x * x) +
                           (y * y));

That way you can see the similarity of squaring x, and squaring y. But breaking up the multiplications because they repeat the x's and y's would be taking it too far -- it always requires balance and thinking about how can I communicate my intent and the SHAPE of the problem I'm solving.

You can also use indentation and formatting to bring out and make obvious other two-dimensional patterns and regularities, especially with two-dimensional cellular automata and graphics code.

There are some examples of that technique in my 2D cellular automata machine code. (There's a lot of code, so don't be overwhelmed, since I've been working on that since around 1985 or so. So never give up and just stick to it! You don't need to understand the code, which is pretty esoteric, just notice the patterns.)

https://github.com/SimHacker/CAM6/blob/master/javascript/CAM...

                        // Load the right two columns of the 3x3 window.
                        n  = cells[cellIndex - nextCol - nextRow];  ne = cells[cellIndex - nextRow];
                        c  = cells[cellIndex - nextCol          ];  e  = cells[cellIndex          ];
                        s  = cells[cellIndex - nextCol + nextRow];  se = cells[cellIndex + nextRow];

[...]

                            // Scroll the 3x3 window to the right, scrolling the middle and right
                            // columns to the left, then scooping up three new cells from the right
                            // leading edge.
                            nw = n;  n = ne;  ne = cells[cellIndex + nextCol - nextRow];
                            w  = c;  c =  e;  e  = cells[cellIndex + nextCol          ];
                            sw = s;  s = se;  se = cells[cellIndex + nextCol + nextRow];

[...]

                            var sum8 =
                                    (nw & 1) + (n & 1) + (ne & 1) +
                                    (w  & 1) +           (e  & 1) +
                                    (sw & 1) + (s & 1) + (se & 1);

[...]

                                var tableIndex =
                                    (((c         >> plane) & 0x03) <<  0) |
                                    (((e         >> plane) & 0x03) <<  2) |
                                    (((w         >> plane) & 0x03) <<  4) |
                                    (((s         >> plane) & 0x03) <<  6) |
                                    (((n         >> plane) & 0x03) <<  8) |
                                    (((cellX             ) & 0x01) << 10) |
                                    (((cellY             ) & 0x01) << 11) |
                                    (((phaseTime         ) & 0x01) << 12) |
                                    (((plane     >> 1    ) & 0x03) << 13);

[...]

When the sub-expressions won't fit on one line, you can still use indentation to help the reader understand the spatial patterns:

                            error +=
                                (nw * kernelBytes[4 - kernelDown - kernelRight]) +
                                    (n  * kernelBytes[4 - kernelDown]) +
                                        (ne * kernelBytes[4 - kernelDown + kernelRight]) +
                                (w  * kernelBytes[4 - kernelRight]) +
                                    (c  * kernelBytes[4]) +
                                        (e  * kernelBytes[4 + kernelRight]) +
                                (sw * kernelBytes[4 + kernelDown - kernelRight]) +
                                    (s  * kernelBytes[4 + kernelDown]) +
                                        (se * kernelBytes[4 + kernelDown + kernelRight]) +
                                frob;

But that would probably be more readable if I used intermediate variables for the kernelBytes[...] expressions, gave them descriptive names matching their corresponding directions, and lined up all their calculations so you could see the similarities and differences, because right now all the kernleBytes[4 +/- kernelDown +/- kernelRight] expressions are jumbled around horizontally, when they could be lined up nicely by themselves if they weren't interleaved with the n/s/e/w/ne/nw/sw/se multiplications. (I'll leave that improvement as an exercise for the reader. ;)

The point is you want to vertically line up as many of the repeated letters and symbols in nice neat columns as possible, so that your eye can easily see which are the same, and the ones that aren't stand out obviously so you can ignore all the same things and focus on the differences. (Which are typically variations like +/-, sin/cos, x/y/z, -1/0/1, nw/n/ne/w/c/e/sw/s/se, and longer series of names and numbers. Whatever changes should stick out visually, and be lined up and grouped together so your eyes can scan over them!)

That not only helps other people understand your code, but it also helps you be sure that you wrote the right thing, what you actually meant, instead of making an easy to miss typo.

The difficulty of reading you own code, which is HARD even for professional programmers, is that you usually see what you MEANT to write, not what you actually wrote. That's why it's so important to get other people to read your code, and to read other people's code, like pair programming and code reviews, or even explaining it to a duck.

https://en.wikipedia.org/wiki/Rubber_duck_debugging

It takes a lot more effort and concentration to slow down and look at what's actually there, instead of what you want to be there. (That's true for many aspects of life...)

it infuriated me that Google's style guides forbid this kind of formatting. Anytime someone else tried to get them to allow it it was shouted down as ASCII art

  • If I'm honest, I like that kind of formatting for small 2-3 line, 2-3 part expressions. But I am not a big fan for it in much bigger parts (like that huge index calculation above). I always see them and think that I'd rather see a loop over an array of values, or split into many smaller expressions.