Saving Vertical Space

Named captures, ternary operator, declaring variables on the same line, not initializing the $total (there is no need). Reduces the function length by 25% and is still legible IMO (and no line is > than 80 chars for the old school-ists):

sub roll {
    my $input = shift;
    return if $input !~ /(?<num>\d*)d(?<die>\d+)\s*(?<op>\D?)\s*(?<end>\d*)/;
    my (@dice,$total);
    push @dice, int(rand($+{die}))+1 for ( 1..$+{num} );
    if ( $+{op} eq 'b' ) {
        $+{end} = $+{num} if $+{end} > $+{num};
        @dice = sort { $b <=> $a } @dice;
        $#dice = $+{end}-1;
    }
    $total += $_ for @dice;
    return $+{op} eq '+' ? $total + $+{end} : $+{op} eq '-' ? $total - $+{end}
         : $+{op} eq '*' ? $total * $+{end} : $+{op} eq '/' ? $total / $+{end}
         : $total;
}

But i wouldn't write it like that, i would write it like this as i like to be generous with both vertical and horizontal space. I also like to be very lisp like with parentheses to prevent ambiguity and obscure precedence bugs:

sub roll {

    my ( $input ) = @_;

    return if $input !~ /(?<num>\d*)d(?<die>\d+)\s*(?<op>\D?)\s*(?<end>\d*)/;

    my ( @dice,$total );

    push( @dice, int( rand( $+{die} ) ) + 1 ) for ( 1 .. $+{num} );

    if ( $+{op} eq 'b' ) {

        $+{end} = $+{num} if $+{end} > $+{num};
        @dice   = sort { $b <=> $a } @dice;
        $#dice  = $+{end} - 1;
    }

    $total += $_ for @dice;

    return $+{op} eq '+' ? $total + $+{end}
         : $+{op} eq '-' ? $total - $+{end}
         : $+{op} eq '*' ? $total * $+{end}
         : $+{op} eq '/' ? $total / $+{end}
         : $total;
}

FWIW here's the (pretty poor) tests i wrote before refactoring this function:

use strict;
use warnings;

use Test::Most;

my $result = roll( "10d10+10" );

like( $result,qr/^\d+$/,'roll' );
ok( $result > 0,' .. > 0' );
ok( $result < 100,' .. < 100' );

ok( defined roll( "10d10-10" ),'-10' );
ok( defined roll( "10d10*10" ),'*10' );
ok( defined roll( "10d10/10" ),'/10' );
ok( defined roll( "10d10b2" ),'b2' );

note $result;
note roll( "10d10-10" );
note roll( "10d10*10" );
note roll( "10d10/10" );
note roll( "10d10b2" );

If you're going to refactor a function to clean it up then always write tests, because it's so easy to introduce bugs. I may well have done in my attempt above, since the tests i wrote are no where near comprehensive.

/r/perl Thread Link - tinypig2.blogspot.com