質問

Is there a simple heuristic for understanding how to read nested ternary operators? I came across this in someone's source code and can't grok it. A simple ternary is easy:

isRed = color == 'red' ? true : false

But how do you read the following? Can I just line up the first with the last and the second with the second to last, or must I parse this into an if/else tree in my head?

var offset =
  ( hasFrozenRows )
    ? ( options.frozenBottom )
    ? ( row >= actualFrozenRow )
    ? ( h < viewportTopH )
    ? ( actualFrozenRow * options.rowHeight )
    : h
    : 0
    : ( row >= actualFrozenRow )
    ? frozenRowsHeight
    : 0
    : 0;

Retabbed, it can look like this, which is almost understandable (?)

      var offset =
        ( hasFrozenRows ) ?
          ( options frozenBottom ) ?
            ( row >= actualFrozenRow ) ?
              ( h < viewportTopH ) ?
                ( actualFrozenRow * options.rowHeight )
                :
                h
              :
              0
            :
            ( row >= actualFrozenRow ) ?
              frozenRowsHeight
              :
              0
            :
            0;
役に立ちましたか?

解決

I think you could have more luck if you try to read it as a series of check this, if true then this, else that.

For this, it might be easier to put the ? and : operators at the beginning of lines, and read them as if they were arrows on a flowchart, labeled "yes" and "no". e.g.:

cond1 
  ? cond2 
    ? cond3 
      ? res1 
      : res2
    : res3
  : res4

Which could be read as:

cond1?
  yes -> is cond2 
    yes -> is cond3?
      yes -> res1 
      no -> res2
    no -> res3
  no -> res4

That still doesn't make this very readable, and I agree with all the comments saying this kind of code should really be rewritten to be readable.

他のヒント

For something messy like this you have to work from the outside in. Essentially, what this maps to is this:

if ( hasFrozenRows ) {
  if ( options.frozenBottom ) {
    if ( row >= actualFrozenRow ) {
      if ( h < viewportTopH ) {
        return ( actualFrozenRow * options.rowHeight )
      } else {
        return h;
      }
    } else {
      return 0;
    }
  } else {
    if ( row >= actualFrozenRow ) {
      return frozenRowsHeight
    } else {
      return 0
    }
  }
} else {
  return 0;
}

And I thought the legacy code I worked with was a nightmare...

I recommend running this through a unit tester, like Jasmine, and compare the outputs with the original code to confirm that it's the same.

must I parse this into an if/else tree in my head?

Yes, because in this case it is a tree and not simply chained operators. Those would be easy to understand :-) Without indentation, this definitely needs refactoring.

In this particular case, it also would be helpful to negate the conditions, making reading a lot easier as it puts the condition and the effect directly next to each other:

var offset = (!hasFrozenRows)
               ? 0
               : (!options.frozenBottom)
                 ? (row < actualFrozenRow)
                   ? 0
                   : (h < viewportTopH)
                     ? actualFrozenRow * options.rowHeight
                     : h
                 : (row >= actualFrozenRow)
                   ? frozenRowsHeight
                   : 0;

We also could move the duplicate row >= actualFrozenRow comparison one level up:

var offset = (!hasFrozenRows)
             ? 0
             : (row < actualFrozenRow)
               ? 0
               : (!options.frozenBottom)
                 ? frozenRowsHeight
                 : (h < viewportTopH)
                   ? actualFrozenRow * options.rowHeight
                   : h;

…which actually renders it completely understandable, even if not negated:

var offset = ( hasFrozenRows )
             ? ( row >= actualFrozenRow )
               ? ( options.frozenBottom )
                 ? ( h < viewportTopH )
                   ? actualFrozenRow * options.rowHeight
                   : h
                 : frozenRowsHeight
               : 0
             : 0;

You now can also see that you might merge the first two conditions to hasFrozenRows && row >= actualFrozenRow.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top