I'm posting this because it's a demonstration of an interesting feature in Perl that can cause strange bugs. I worked and worked at finding out what was wrong with the following snippet. It's a subtle bug and I'm not too embarrassed at missing it. It's a mistake I hopefully won't repeat. Can you find it? If you find it too quickly, don't spoil it for the others, though :)
while ( my $data = $t_sth->fetchrow_arrayref ) { my ( $amt, $id ) = @$data; $amt /= PRECISION; SWITCH: { $id == $CASH && ($tcash += $amt) && last SWITCH; $id == $ACCOUNT && ($taccount += $amt) && last SWITCH; $id == $CHECK && ($tcheck += $amt) && last SWITCH; $id == $GIFT && ($tgift += $amt) && last SWITCH; $id == $VOUCHER && ($tvoucher += $amt) && last SWITCH; $id == $CC_MAN_AUTH && ($tcc_man_auth += $amt) && last SWITCH; ($tcredit += $amt); } }
Hint: the bug is in the switch statement. All data entering the switch statement is correct. Further, $id and the values it compares with are both integers (i.e., no floating point comparisons).
chromatic was shown this and he rewrote it with this. Bonus points if you see how it works.
my %totals; @totals{$CASH, $ACCOUNT, $CHECK, $GIFT, $VOUCHER, $CC_MAN_AUTH} = \($tcash, $taccount, $tcheck, $tgift, $tvoucher, $tcc_man_auth); while ( my $data = $t_sth->fetchrow_arrayref ) { my ( $amt, $id ) = @$data; $amt /= PRECISION; if (my $totalvar = $totals{ $id }) { $$totalvar += $amt; } else { $tcredit += $amt; } }
His is a better solution than mine in that it works, but discovering that taking a reference is a distributive action (the assignment to the hash slice) was surprising! He assures me that this is a documented feature.
Update: Congrats to dws for being the first person to accurately identify and describe the bug.
Cheers,
Ovid
Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.
In reply to Pop quiz: find the bug by Ovid
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |