ControlBreak
view release on metacpan or search on metacpan
Return the level number for the most recent invocation of the test()
method.
level_numbers
Return a list of level numbers corresponding to the levels defined in
new(). This can be useful, for example, when you want to set up some
lexical variables for use as indexes into a list you might use to
accumulate subtotals.
my $cb = ControlBreak->new( qw( L1 L2 EOD ) );
my @totals;
my ($L1, $L2, $EOD) = $cb->level_numbers;
foreach my $sublist (@list_of_lists) {
my ($control1, $control2, $number) = $sublist->@*;
...
my $sub_totals = sub {
if ($cb->break('L1')) {
# report the L1 subtotal here
$totals[$L1] = 0; # clear the subtotal
}
...
# accumulate subtotals
map { $totals[$_] += $number } $cb->level_numbers;
};
$cb->test_and_do(
$control1,
$control2,
$cb->iteration == $list_of_lists - 1,
$sub_totals
);
}
reset
Resets the state of the object so it can be used again for another set
of iterations using the same number and type of controls establish when
the object was instantiated with new(). Any comparisons that were
subsequently modified are retained.
test ( $var1 [, $var2 ]... )
Submits the control variables for testing against the values from the
previous iteration.
Testing is done in reverse order, from highest to lowest (major to
minor) and stops once a change is detected. Where it stops determines
the control break level. For example, if $var2 changed, method levelnum
will return 2. If $var2 did not change, but $var1 did, then method
levelnum() will return 1. If nothing changes, then levelnum() will
return 0.
Note that the level numbers set by test(...) are true if there was a
level change, and false if there wasn't. So, they can be used as a
simple boolean test of whether there was a change. Or you can use the
break() method to determine whether any control break has occurred.
Because level numbers correspond to the hierarchical data order, they
can be use to trigger multiple actions; e.g. levelnum() >= 1 could be
used to print subtotals for levels 1 whenever a control break occurred
for level 1, 2 or 3. It is usually the case that higher control breaks
are meant to cascade to lower control levels and this can be achieved in
this fashion. The break() method simplifies this.
Note that method continue() must be called at the end of each iteration
in order to save the values of the iteration for the next iteration. If
not, the next test(...) invocation will croak.
test_and_do ( $var1 [, $var2 ]... $var_end, $coderef )
The test_and_do() method is similar to test(). It takes the same
arguments as test(), plus one additional argument that is an anonymous
code reference. Internally, it calls test() and then, if there is a
control break, calls the anonymous subroutine provided in the last
argument. Typically, that code will perform work related to subtotals or
other actions necessary when a control break occurs.
But test_and_do() does one other thing. It expects the last control
variable ($var_end) to be an end of data indicator, such as the perl
builtin operator eof. This indicator should return false on each
iteration over the data until the very last iteration -- when it should
change to true, thereby triggering a major control break.
What test_and_do does then is to add an extra loop. This simulates a
final record and will trigger test() to signal control breaks at all
levels. Thus, the code provided will be executed between every change of
data AND after all data has been iterated over.
This avoids the necessity of repeating the control break actions you've
put inside the data loop immediately after the loop's closing bracket.
When you just use test and continue(), an end-of-data control break
won't occur and the simplest workaround is to just duplicate your
control break code after the loops closing bracket.
Here's a typical use case involving end of file processing. Note the
extra control level, named 'EOF', and the use of the eof builtin
function as the second last argument of test_and_do():
my $cb = ControlBreak->new( qw( L1 L2 EOF ) );
my $lev1_subtotal = 0;
my $lev2_subtotal = 0;
my $grand_total = 0;
while (my $line = <>) {
chomp $line;
my ($lev1, $lev2, $data) = split "\t", $line;
my $subtotal_coderef = sub {
if ($cb->break('L1')) {
say $cb->last('L1'), $cb->last('L2'), $lev1_subtotal . '*';
$lev1_subtotal = 0;
}
...
if ($cb->break('EOF')) {
say 'Grand total,,', $grand_total, '***';
}
$lev1_subtotal += $data;
$lev2_subtotal += $data;
$gran_total += $data;
}
( run in 2.356 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )