Re^3: Two simple code style advice questions (no reassembly required)by tye (Sage)
|on Jan 18, 2013 at 17:25 UTC||Need Help??|
Of course, I didn't say it would be easier for you to parse.
I can parse the three lines completely in a single glance, not having to pause to even wonder if the details of some complex expression are worth worrying about yet or not. The whole point of the code is already tucked away in my head after that one glance.
But, no, I don't usually parse the flow while ignoring nearby details, because I find it rather time-consuming to construct the vague but complex (for me) "initializes $mol to something, but I haven't bothered to figure out what" concept. And such vague bits would just leave a mental snag to trip over repeatedly as I discern the purpose while mentally going over the flow. I find the factored-out bits need at least a nice name for me to be able to smoothly gloss over them when considering the flow. More often, I will understand some details of the factored-out bit before dismissing those details from consideration of the current flow (usually by assigning a decent name to that bit -- which is why subroutines are such a good way to factor out bits of complexity; they each get assigned a name).
The expression is complex enough that I can't tell that it is simply initializing the variable to one of a few different values just by glancing at it. Once I start to parse the expression it takes more effort than a glance. My eyes have to focus on several parts and move back and forth matching up the bits to construct the meaning. By the time I've parsed it enough to tell that it isn't calling some subroutine (that might do complex work), I've already spent more time on the one line then I would have spent on the several lines and yet I still don't understand what the code is doing.
Both of my multi-line versions parse effortlessly for me. I am not aware of even moving my eyes during the parsing. My mental focus smoothly shifts to each line, in order, instantly understanding the full code of the line and then the next line neatly snaps another detail onto the mental model without the need for any backtracking or restructuring.
The worst problem of the relative bloat in number of lines is if the logical block of code (almost always a subroutine) gets pushed beyond "one screen full" and thus can't be parsed with just simple and fast eye movements. And that would usually lead to me being less "extravagant" or to factoring out some logical sub-section.
That is a relative strain to parse in comparison. My experience is that matching up parens is one of the most time-consuming parsing tasks for humans (I've tested it using slide shows and even people who claim to match up parens easily can't do it quickly, IME). The constructs involved are not visually apparent. I'm forced to individually recognize single characters and then mentally re-assemble the logical structure from too many tiny pieces.
This all reminds me of why newspapers print in rather narrow columns and how many people can read such very quickly without their eyes zigging back and forth (I don't think I'm one of those people, though).
There are two ways I might try to parse that one complex line. The way that always works is to parse the components of it in order. That is slow because the number of parts to parse is much larger (for me) in the one-line case than in the multi-line cases.
Using newlines to show each mental pause point, the code ends up in my head like:
And then I have to backtrack several times to line up the '(' and the ')' and to line up the 2nd ':' with the first '?' and then it isn't obvious to me when which of the three values get chosen until I mentally simulate running the code, putting the conditions and values into proper association as I go.
The second method is to glance over the code, recognizing the "obvious" bits and then filling in the gaps between them. That, for me, starts out as:
Then I have to visually and mentally jump between the two "noise" piles and (frustratingly) spend mental effort dealing with single characters. Worse, I then have to tie single characters that aren't next to each other together in a complex structure that isn't represented visually.
My last example gets parsed so smoothly for me. It is just 4 simple visual pieces that are also 4 simple mental pieces:
The second piece is the best example of why I prefer my version to yours:
'n/a' is the value that represents "not defined". This is so very much more obscured in your version of the code. It is a single visual and mental chunk in my versions. No re-assembly required.
Your mileage may vary, obviously.
Note that I'm talking tiny optimizations here. It might take me 1 or even 2 seconds to parse your complex line. But when reading code, spending that much time on a line is a very long time. When the lines aren't complex, I can scan a whole screen of code in 1 second and understand it.
And it isn't that I find your most complex line of code unacceptable or even fundamentally difficult to parse. I'd even write the following:
if I had a well-factored subroutine that barely fit on a screenful of lines with that as one of them.
But if I got the subroutine factored to be smaller, I'd get extravagant with those short expressions and be so happy to re-parse and understand the whole subroutine without even having to degrade out of just needing quick glances.