Re: Measuring Complexity (was Re: Perl::Metrics::Simple 0.30)
On Dec 19, 2006, at 2:43 PM, Matisse Enzer wrote: I agree that ( $a == $b ) is not an extra branch, but, it is harder for a human to understand than ( $a ) When did this thread change from code complexity to human understandability? Those are rather different topics. ( $a ) of course translates to ( defined $a && $a ne '' && $a ne '0' ) which is likely harder to understand than ( $a == $b ). Undoubtably, the former leads to more subtle bugs than the latter. I think that for true Cyclomatic Complexity ($a==$b) is the same as ($a) but there is some other thing going on with ($a == $b) that perhaps, should be considered some other kind of complexity. You're likely biting off more than you can chew. I recommend that you take a lesson from Kwalitee and focus on implementing easily- defined metrics first. Chris -- Chris Dolan, Software Developer, http://www.chrisdolan.net/ Public key: http://www.chrisdolan.net/public.key vCard: http://www.chrisdolan.net/ChrisDolan.vcf
Re: Measuring Complexity (was Re: Perl::Metrics::Simple 0.30)
On Dec 17, 2006, at 9:02 PM, Randy W. Sims wrote: Matisse Enzer wrote: On Dec 15, 2006, at 10:13 PM, Chris Dolan wrote: OK, I see. Perhaps I was distracted from your main point by mention of cyclomatic complexity, which has a rather specific definition. Mea culpa. In the next release I will change the documentation for Perl::Metrics::Simple to make clear how exactly the complexity score is calculated, and that it is only an approximation of true cyclomatic complexity. Meanwhile, I am open to making the measurement more useful. For example, should if ($a eq $b) or if ( $a == $b ) be counted as more complex than: if ( $a ) ? I don't think so. The latter is simply an implicit version of the former. It means something like: if ( $a == $true_value || $a eq $true_value ) Another way of looking at it is that comparison operators are no different than any other expressions. if ( func() ) if ( --$a ) The expressions themselves do not represent branches. They just produce values that may be used as the basis for a branch. I agree that ( $a == $b ) is not an extra branch, but, it is harder for a human to understand than ( $a ) I think that for true Cyclomatic Complexity ($a==$b) is the same as ($a) but there is some other thing going on with ($a == $b) that perhaps, should be considered some other kind of complexity. --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Re: Perl::Metrics::Simple 0.30
On 12/10/06, Matisse Enzer <[EMAIL PROTECTED]> wrote: Now in a CPAN near you: Perl::Metrics::Simple 0.30 Ah, the old Simple versus Basic argument: http://search.cpan.org/dist/Perl-Metric-Basic/ Leon, who has already renamed it once to please Adam.
Re: Measuring Complexity (was Re: Perl::Metrics::Simple 0.30)
Matisse Enzer wrote: On Dec 15, 2006, at 10:13 PM, Chris Dolan wrote: OK, I see. Perhaps I was distracted from your main point by mention of cyclomatic complexity, which has a rather specific definition. Mea culpa. In the next release I will change the documentation for Perl::Metrics::Simple to make clear how exactly the complexity score is calculated, and that it is only an approximation of true cyclomatic complexity. Meanwhile, I am open to making the measurement more useful. For example, should if ($a eq $b) or if ( $a == $b ) be counted as more complex than: if ( $a ) ? I don't think so. The latter is simply an implicit version of the former. It means something like: if ( $a == $true_value || $a eq $true_value ) Another way of looking at it is that comparison operators are no different than any other expressions. if ( func() ) if ( --$a ) The expressions themselves do not represent branches. They just produce values that may be used as the basis for a branch. Randy.
Re: Measuring Complexity (was Re: Perl::Metrics::Simple 0.30)
On Dec 16, 2006, at 12:07 PM, Matisse Enzer wrote: On Dec 15, 2006, at 10:13 PM, Chris Dolan wrote: OK, I see. Perhaps I was distracted from your main point by mention of cyclomatic complexity, which has a rather specific definition. Mea culpa. In the next release I will change the documentation for Perl::Metrics::Simple to make clear how exactly the complexity score is calculated, and that it is only an approximation of true cyclomatic complexity. Meanwhile, I am open to making the measurement more useful. For example, should if ($a eq $b) or if ( $a == $b ) be counted as more complex than: if ( $a ) *shrug* It's really up to you and your user base, as long as you are consistent. Certainly the former examples require more tests to get full coverage. Personally, I use Devel::Cover's concept of complexity implicitly. Chris -- Chris Dolan, Software Developer, http://www.chrisdolan.net/ Public key: http://www.chrisdolan.net/public.key vCard: http://www.chrisdolan.net/ChrisDolan.vcf
Measuring Complexity (was Re: Perl::Metrics::Simple 0.30)
On Dec 15, 2006, at 10:13 PM, Chris Dolan wrote: OK, I see. Perhaps I was distracted from your main point by mention of cyclomatic complexity, which has a rather specific definition. Mea culpa. In the next release I will change the documentation for Perl::Metrics::Simple to make clear how exactly the complexity score is calculated, and that it is only an approximation of true cyclomatic complexity. Meanwhile, I am open to making the measurement more useful. For example, should if ($a eq $b) or if ( $a == $b ) be counted as more complex than: if ( $a ) ? -M --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Re: Perl::Metrics::Simple 0.30
On Dec 15, 2006, at 10:22 PM, Matisse Enzer wrote: On Dec 15, 2006, at 7:52 AM, Chris Dolan wrote: That can't be right. Negation does not contribute to complexity. I think it is fair to say, that to a human, negation *can* increase complexity: if ( $foo ) { # do something } is a little bit easier to understand than: if ( ! $foo ) { # do something } Instead, I believe it is the for loop and the exit points that are increasing your count. It certainly is possible to create a better, more sophisticated measure of cyclomatic complexity - and I think you are probably right, or at least "more right" in the way you are suggesting complexity be counted. Perl::Metrics::Simple simply gives one "point" for each of the following: qw( ! && || ||= &&= or and xor not ? <<= >>= ); qw( for foreach goto if else elsif last next unless until while ); See themeasure_complexity() method in Perl::Metrics::Simple::Analysis::File OK, I see. Perhaps I was distracted from your main point by mention of cyclomatic complexity, which has a rather specific definition. Chris -- Chris Dolan, Software Developer, http://www.chrisdolan.net/ Public key: http://www.chrisdolan.net/public.key vCard: http://www.chrisdolan.net/ChrisDolan.vcf
Re: Perl::Metrics::Simple 0.30
On Dec 15, 2006, at 7:52 AM, Chris Dolan wrote: That can't be right. Negation does not contribute to complexity. Instead, I believe it is the for loop and the exit points that are increasing your count. Consider rewriting the for as ifs and gotos: sub complexity_of_six { my $bar = shift; my $total = 0; my $type = ref $bar; if ( ! $type ) { $total = $bar; } elsif ( $type eq 'ARRAY' ) { my $_i = 0; LOOP: goto DONE if ($_i >= @{$bar}); my $baz = $bar->[$_i]; $total += $baz; $_i++; goto LOOP; DONE: } else { confess("Don't know how to handle refs to $type"); } return $total; } Just for the record, Perl::Metrics::Simple gives that code a complexity count of 8. I realize that I'm not actually counting 'ne', 'eq', 'ge', or 'le', which is probably a bug :-) I'm totally interested in better way(s) to measure this by the way. --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Re: Perl::Metrics::Simple 0.30
On Dec 15, 2006, at 7:52 AM, Chris Dolan wrote: That can't be right. Negation does not contribute to complexity. I think it is fair to say, that to a human, negation *can* increase complexity: if ( $foo ) { # do something } is a little bit easier to understand than: if ( ! $foo ) { # do something } Instead, I believe it is the for loop and the exit points that are increasing your count. It certainly is possible to create a better, more sophisticated measure of cyclomatic complexity - and I think you are probably right, or at least "more right" in the way you are suggesting complexity be counted. Perl::Metrics::Simple simply gives one "point" for each of the following: qw( ! && || ||= &&= or and xor not ? <<= >>= ); qw( for foreach goto if else elsif last next unless until while ); See themeasure_complexity() method in Perl::Metrics::Simple::Analysis::File --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Re: Perl::Metrics::Simple 0.30
Chris Dolan wrote: > That can't be right. Negation does not contribute to complexity. > Instead, I believe it is the for loop and the exit points that are > increasing your count. Consider rewriting the for as ifs and gotos: > >sub complexity_of_six { >my $bar = shift; >my $total = 0; >my $type = ref $bar; >if ( ! $type ) { >$total = $bar; >} >elsif ( $type eq 'ARRAY' ) { >my $_i = 0; > LOOP: >goto DONE if ($_i >= @{$bar}); >my $baz = $bar->[$_i]; >$total += $baz; >$_i++; >goto LOOP; > DONE: >} >else { >confess("Don't know how to handle refs to $type"); >} >return $total; >} > > Then the decision points are: > 1) if ( ! $type ) > 2) if ( $type eq 'ARRAY') > 3) if ($_i >= @{$bar}) > 4) else > and the end points are > 1) confess("Don't know how to handle refs to $type"); > 2) return $total > > So I actually count a complexity of 7 (num decision points + num > endpoints + 1) if I've understood the wiki definition correctly. > http://en.wikipedia.org/wiki/Cyclomatic_complexity Are the else and confess different decision points? There's no way out of the else but the confess. Similar issue with the return.
Re: Perl::Metrics::Simple 0.30
On Dec 14, 2006, at 11:20 PM, Matisse Enzer wrote: On Dec 14, 2006, at 3:05 PM, Michael G Schwern wrote: Matisse Enzer wrote: sub complexity_of_six { my $bar = shift; my $total = 0; my $type = ref $bar; if ( ! $type ) { $total = $bar; } elsif ( $type eq 'ARRAY' ) { foreach my $baz ( @{$bar} ) { $total += $baz; } } else { confess("Don't know how to handle refs to $type"); } return $total; } I'm missing something, I only count 3 paths. if, elsif and else. The confess() might count (die/throw exception) but its the only path through the else. Same with the loop, its the only path through the elsif. I over-simplified my explanation. It's not exactly paths-through- the-code. The complexity "points" in that example are: 1 for the subroutine itself. 1 for the "if" 1 for the ! logic operator in the if condition 1 for the elsif 1 for the "ne" logic operator in the elsif condition 1 for the else 6 total That can't be right. Negation does not contribute to complexity. Instead, I believe it is the for loop and the exit points that are increasing your count. Consider rewriting the for as ifs and gotos: sub complexity_of_six { my $bar = shift; my $total = 0; my $type = ref $bar; if ( ! $type ) { $total = $bar; } elsif ( $type eq 'ARRAY' ) { my $_i = 0; LOOP: goto DONE if ($_i >= @{$bar}); my $baz = $bar->[$_i]; $total += $baz; $_i++; goto LOOP; DONE: } else { confess("Don't know how to handle refs to $type"); } return $total; } Then the decision points are: 1) if ( ! $type ) 2) if ( $type eq 'ARRAY') 3) if ($_i >= @{$bar}) 4) else and the end points are 1) confess("Don't know how to handle refs to $type"); 2) return $total So I actually count a complexity of 7 (num decision points + num endpoints + 1) if I've understood the wiki definition correctly. http://en.wikipedia.org/wiki/Cyclomatic_complexity Chris -- Chris Dolan, Software Developer, Clotho Advanced Media Inc. 608-294-7900, fax 294-7025, 1435 E Main St, Madison WI 53703 vCard: http://www.chrisdolan.net/ChrisDolan.vcf Clotho Advanced Media, Inc. - Creators of MediaLandscape Software (http://www.media-landscape.com/) and partners in the revolutionary Croquet project (http://www.opencroquet.org/)
Re: Perl::Metrics::Simple 0.30
On Dec 14, 2006, at 3:05 PM, Michael G Schwern wrote: Matisse Enzer wrote: sub complexity_of_six { my $bar = shift; my $total = 0; my $type = ref $bar; if ( ! $type ) { $total = $bar; } elsif ( $type eq 'ARRAY' ) { foreach my $baz ( @{$bar} ) { $total += $baz; } } else { confess("Don't know how to handle refs to $type"); } return $total; } I'm missing something, I only count 3 paths. if, elsif and else. The confess() might count (die/throw exception) but its the only path through the else. Same with the loop, its the only path through the elsif. I over-simplified my explanation. It's not exactly paths-through-the- code. The complexity "points" in that example are: 1 for the subroutine itself. 1 for the "if" 1 for the ! logic operator in the if condition 1 for the elsif 1 for the "ne" logic operator in the elsif condition 1 for the else 6 total --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Re: Perl::Metrics::Simple 0.30
Matisse Enzer wrote: >sub complexity_of_six { >my $bar = shift; >my $total = 0; >my $type = ref $bar; >if ( ! $type ) { >$total = $bar; >} >elsif ( $type eq 'ARRAY' ) { >foreach my $baz ( @{$bar} ) { >$total += $baz; >} >} >else { >confess("Don't know how to handle refs to $type"); >} >return $total; >} I'm missing something, I only count 3 paths. if, elsif and else. The confess() might count (die/throw exception) but its the only path through the else. Same with the loop, its the only path through the elsif. Also... sub foo { if( ... ) { ... } else { ... } return ... } Complexity of two or three? I count only two possible paths.
Re: Perl::Metrics::Simple 0.30
On Dec 10, 2006, at 1:16 AM, Ovid wrote: --- Matisse Enzer <[EMAIL PROTECTED]> wrote: McCabe Complexity - Code not in any subroutine:: min: 1 max 10 mean: 1.00 std. deviation: 2.54 median: 1.00 Subroutines/Methods: min: 1 max: 15 avg: 1.00 std. deviation: 2.60 median: 1.00 Hi Matisse, Thanks or creating this code. It looks interesting, though I confess I'm feeling a little slow. In reading through the Wikipedia description and http://www.sei.cmu.edu/str/descriptions/cyclomatic_body.html, I still don't understand how to interpret the above result. I think the easy way to understand Cyclomatic Complexity is this: It is a measurement of how many possible paths there are through a chunk of code. So, if there are 3 possible paths through a subroutine then it has a complexity of 3. If you add one "if" statement then the complexity goes up by 1. There are more sophisticated explanations, and, more sophisticated ways of measuring complexity than what my module does, but I think you get the idea: sub complexity_of_one { my $bar = shift; return $bar * 23; } sub complexity_of_two { my $bar = shift; if ( $bar->isa('Foo::Bar') ) { return $bar->multiply(23); } return $bar * 23; } sub complexity_of_two { my $bar = shift; if ( $bar->isa('Foo::Bar') ) { return $bar->multiply(23); } return $bar * 23; } sub complexity_of_six { my $bar = shift; my $total = 0; my $type = ref $bar; if ( ! $type ) { $total = $bar; } elsif ( $type eq 'ARRAY' ) { foreach my $baz ( @{$bar} ) { $total += $baz; } } else { confess("Don't know how to handle refs to $type"); } return $total; } Also, if your average (mean) is 1.00, how is the standard deviation greater than that value? You fund a bug :-) The countperl script v0.30 is reporting the median, not the mean for the complexity numbers. I'll fix this and release a new version today or tomorrow. Thanks ! Here's a portion of the corrected report: ./countperl lib/TAPx Perl files found:12 Counts -- total code lines:1959 lines of non-sub code: 1261 packages found: 14 subs/methods:98 Subroutine/Method Size -- min: 1 lines max: 48 lines mean: 7.12 lines std. deviation: 8.24 median: 4.00 McCabe Complexity - Code not in any subroutine:: min: 1 max 10 mean: 2.17<--- FIXED std. deviation: 2.54 median: 1.00 Subroutines/Methods: min: 1 max: 15 mean: 2.42<--- FIXED std. deviation: 2.60 median: 1.00 Tab-delimited list of subroutines, with most complex at top --- complexity sub pathsize 15 _initialize lib/TAPx/Parser.pm 48 11 _finish lib/TAPx/Parser.pm 30 11 _switches lib/TAPx/Parser/Source/Perl.pm 22 10 _aggregate_results lib/TAPx/Parser.pm 11 10 {code not in named subroutines} lib/TAPx/Parser.pm 626 9 nextlib/TAPx/Parser/Iterator.pm 27 8 _filtered_inc lib/TAPx/Parser/Source/Perl.pm 15 Etc. -M --- Matisse Enzer <[EMAIL PROTECTED]> http://www.matisse.net/ - http://www.eigenstate.net/
Perl::Metrics::Simple 0.30
Now in a CPAN near you: Perl::Metrics::Simple 0.30 Installs thecountperlscript which you point at one or more Perl files (and/or directories) and get a report of all the subroutines, sorted by complexity. Line counts exclude both comments and pod. For example: % countperl lib/TAPx/ Perl files found:12 Counts -- total code lines:1959 lines of non-sub code: 1261 packages found: 14 subs/methods:98 Subroutine/Method Size -- min: 1 lines max: 48 lines mean: 7.12 lines std. deviation: 8.24 median: 4.00 McCabe Complexity - Code not in any subroutine:: min: 1 max 10 mean: 1.00 std. deviation: 2.54 median: 1.00 Subroutines/Methods: min: 1 max: 15 avg: 1.00 std. deviation: 2.60 median: 1.00 Tab-delimited list of subroutines, with most complex at top --- complexity sub pathsize 15 _initialize lib/TAPx/Parser.pm 48 11 _finish lib/TAPx/Parser.pm 30 11 _switches lib/TAPx/Parser/Source/Perl.pm 22 10 _aggregate_results lib/TAPx/Parser.pm 11 10 {code not in named subroutines} lib/TAPx/Parser.pm 626 9 nextlib/TAPx/Parser/Iterator.pm 27 8 _filtered_inc lib/TAPx/Parser/Source/Perl.pm 15 7 BEGIN lib/TAPx/Parser.pm 39 6 nextlib/TAPx/Parser.pm 22 6 run lib/TAPx/Parser.pm 15 6 _lexlib/TAPx/Parser.pm 18 6 switcheslib/TAPx/Parser/Source/Perl.pm 16 5 BEGIN lib/TAPx/Parser/Aggregator.pm 22 5 _make_plan_tokenlib/TAPx/Parser/Grammar.pm 17 4 _tokens lib/TAPx/Parser.pm 15 4 _check_ending_plan lib/TAPx/Parser.pm 11 4 _initialize lib/TAPx/Parser/Aggregator.pm 11 4 add lib/TAPx/Parser/Aggregator.pm 15 4 _get_parserslib/TAPx/Parser/Aggregator.pm 10 4 new lib/TAPx/Parser/Iterator.pm 14 4 as_string lib/TAPx/Parser/Results/Test.pm 15 4 {code not in named subroutines} lib/TAPx/Parser/ Grammar.pm 132 3 _validate lib/TAPx/Parser.pm 10 3 Test::Builder::failure_output lib/TAPx/Parser/ Builder.pm 8 3 filenamelib/TAPx/Parser/Source/Perl.pm 10 3 get_stream lib/TAPx/Parser/Source/Perl.pm 13 3 _get_commandlib/TAPx/Parser/Source/Perl.pm 9 3 _get_perl lib/TAPx/Parser/Source/Perl.pm 6 3 {code not in named subroutines} lib/TAPx/Parser/ Iterator.pm 45 2 good_plan lib/TAPx/Parser.pm 4 2 parsers lib/TAPx/Parser/Aggregator.pm 7 2 _trim lib/TAPx/Parser/Grammar.pm 6 2 exitlib/TAPx/Parser/Iterator.pm 1 #---output truncated for space---