pdepend: Ternary operator should not be more complex (npath) than a if/else condition.

    function test($a) {
          return $a > 10 ? 5:20;
    }

has only 2 possible paths, but pdepend gives it a npath complexity of 5.

See the discussion at: http://stackoverflow.com/questions/24197973/why-is-the-ternary-operator-more-complex-than-if-else

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 3
  • Comments: 104 (5 by maintainers)

Most upvoted comments

I am sorry, but even if you don’t like/accept/$whatever this definition, it’s the widely accepted and in all tools used formular to calculate the NPath complexity.

Been a real pleasure to read this from the start xD

I’m going to give you five choices, Neo.

It has nothing to do with a matter of taste. It’s a logical issue and it doesn’t add up.

Maybe it’s me missing something stupid, but so far nobody could provide a good explanation for this formula. So as for now it looks like someone took an arbitrary decision 16 years ago and no one bothers to point it out.

I can follow the reasoning of any who propose that ternary expressions have some inherent greater complexity than if-than-else. And if you believe that, then it certainly makes sense to have some complexity metric for your team that reports them as more complex. Maybe that complexity metric is something completely else, or some variant of NPATH; but that complexity is not NPATH. When someone asks for the NPATH they have something specific in mind: the number of acyclic paths through the code. We may believe that ternary expressions, or gotos, are more complex than other forms of expression, but that belief does not change their NPATH values.

This metric is described in an IEEE paper (http://dl.acm.org/citation.cfm?id=42379) and the NPath formular is:

NP(?) = NP((exprl)) + NP((expr2)) + NP((expr3)) + 2

and this paper also has an explanation for you:

The 2 that is included in the NP(?) expression reflects the 
execution path complexity resulting from this statement (i.e., 
one path is traversed if (expr 1 ) is True, and another path 
traversed if (expr 1 ) is False).

which sounds totally valid and correct for me.

why is everyone taking nejmeh as gospel? he has some fake degree from some unaccredited uni. he runs a “consulting” biz out of his house. his website was last updated in the 90’s. i emailed him specifically about this months ago and have gotten no response.

all of his “papers” are on managerial buzzwords, and held behind paywalls.

ternary = if/else, as defined by the language, they resolve to the same opcodes. complexity has nothing to do with human readability. the cake is a lie. dogs and cats living together.

whether nejmeh came up with this metric or not (doubtful that he was the first to think of it), it has to be reclaimed and fixed.

For future reference, in case that thread was to disappear:

This is so sad.

If you look past the petty argument and actually look at the code of PDepend it is so obvious that this is a bug. The code maintainer ‘manuelpichler’ is correct and the cited paper is correct. The actual bug is what the (as of now) last poster ‘Radio’ posted. The NPATH for the subexpressions in the ternary operator are calculated incorrectly, but no one in that shitty flame war focuses on that.

For what it’s worth, this is the particular piece of code: (src/main/php/PDepend/Metrics/Analyzer/NPathComplexityAnalyzer.php around line 213)

    foreach ($node->getChildren() as $child) {
        if (($cn = $this->sumComplexity($child)) === '0') {
            $cn = '1';
        }
        $npath = MathUtil::add($npath, $cn);
    }

The implementation goes out of its way to make the NPATH complexity of each subexpression at least 1, which is simply incorrect. The NPATH of an expression should be the number of && and || operators and since a constant or variable has 0 of these operators their NPATH should be 0.

Thought I’d just leave a comment here as I’ve been thinking about this today, and don’t see a great answer to this question above. Consider this code block if (exp) { exp2 } then { exp3 }. This has an NPath complexity of 3, that’s one for evaluating exp, one for the evaluation of exp2 and one for the evaluation of exp3

However, exp ? exp2 : exp3 gives an NPath complexity is 5. We have the same calculations as before, but we have to add an additional 2 to the NPath because we can do value assignments with the ternary form, and so we gain this additional 2 from doing the return values of exp2 and exp3. To clarify, it’s because we can do String x = true ? "test" : "testing". We do not have to consider return in a standard if form, because String x = if (true) { ... } {...} is syntactically invalid.

I hope this helps the next person who comes along…

@hakre if you scroll up I gave some code which I think would explain the ternary paths resulting in 5 after it is expanded. All I am looking for is whether or not my example is indeed what is going on. This is however not supposed to be how the ternary is interpreted. The ternary is supposed to be a shorthand if/else and expand to that.

Humm I don’t have access to the paper, but the extract you took still doesn’t make sense to me. I understand the +2, but not the “NP((exprl)) + NP((expr2)) + NP((expr3))” part. 2 paths, because a ternary operator has two outcomes: true or false, but the three additional paths are coming out of nowhere.

return $a > 10 ? 5:20;

is literally the same as

if($a > 10)
    return 5;
else
    return 20;

There is no reason to have different complexity.