phaedo has asked for the wisdom of the Perl Monks concerning the following question:

When I load large data I like to print running counts or something to show that I'm doing something. How would I print a marker for every 5th object loaded, or any nth number for that fact? How could I code this ouput ". . . 100 . . . 500 . . . 1000", etc.?

Replies are listed 'Best First'.
Re: printing a running count
by monarch (Priest) on Sep 16, 2005 at 04:34 UTC
    $| = 1; # it's important for autoflush to be on my $counter; for ( $counter = 0; $counter < 5000; $counter++ ) { # ... # check, is this the 5th object? if ( ( $counter % 5 ) == 0 ) { printf( "%5d\r", $counter ); } } # clear status print( ( " " x 5 ) . "\n" );

    Using the mod (or %) operator takes the remainder after dividing the given number. So, in this case, taking the remainder after dividing $counter by five is what we want. When the remainder is zero we have every 5th object.

    Printing a number with the \r code instructs the terminal to move the cursor to the beginning of the line without overwriting or going to a new line.

Re: printing a running count
by McDarren (Abbot) on Sep 16, 2005 at 04:48 UTC

    Here is one way to do it:

    #!/usr/bin/perl -w use strict; #my $cnt; my $step = 100; for (1..10000) { #print "$_..." unless (++$cnt % $step); print "$_..." unless ($_ % $step); } print "\n";

    Update: The counter in that code is redundant, so I've edited it. Thanks, [id://fishbot_v2] :)

    -- Darren
Re: printing a running count
by liverpole (Monsignor) on Sep 16, 2005 at 12:14 UTC
    You could use a trick I often use, to display a running count, but on the same line:
    #!/usr/bin/perl -w + # Strict use strict; use warnings; + + # User-defined my $interval = 10; + + # Main program $| = 1; # Flush STDOUT my $i; my $total = 10_000; + for ($i = 0; $i < $total; $i++) { (0 == ($i % $interval)) and printf " Progress %5d/%5d\e[K\r", $i, +$total; # Call your code here &my_subroutine(); } printf " Progress %5d/%5d\e[K\n", $i, $total; + + # Subroutines sub my_subroutine() { # For test purposes, sleep for a random interval of milliseconds my $nmilli = int(rand(1100) / 1000); select(undef, undef, undef, $nmilli); }
    Every time the index variable $i reaches a multiple of $interval (which is 10 in the above program), the line:
    (0 == ($i % $interval)) and printf " Progress %5d/%5d\e[K\r", $i, +$total;
    causes a count to be displayed, along with the total.  The escape sequence "\e[K" erases to the end of the line, and the carriage-return "\r" repositions the cursor at the beginning of the line (which is why I recommend pussing the extra space before the word "Progress").  At the end of the loop, don't forget to rewrite the progress, but this time with "\n" to finally go to a new line.

    You can change the output to your liking; from displaying only the count (with no label) to displaying the percentage complete (eg. printf " Progress %7.3f%%", 100 * $i / $total;"), etc.

Re: printing a running count
by gargle (Chaplain) on Sep 16, 2005 at 06:43 UTC

    Hi,

    update: it's modulus and not modules!

    I recommend the modulus trick of course, but, just to prove TIMTOWTDI:

    #!/usr/bin/perl use strict; use warnings; my $counter = 1; for my $i (1..20) { # do something with your file, p.e. print $i . " "; ++$counter; if ($counter > 5) { print "."; $counter = 1; } }

    So for twenty lines we print four dots (each fifth line).

    --
    if ( 1 ) { $postman->ring() for (1..2); }
Re: printing a running count
by liverpole (Monsignor) on Sep 16, 2005 at 14:07 UTC
    Another technique that it make sense to mention (and figure me if this is "overkill" in your case!), is that of creating a "visual meter".  Here's an example of an object meter, which you can use to manage the display of a progress meter which increments towards a final total.

    You simply create the meter object with my $meter = new meter($total), call it within the loop with $meter->update($i), and finish up by calling it with no arguments $meter->update() (to show 100% completion, and display the final newline).

    Update:  Added auto-flush of STDERR to the package.

    Here's the code:

    #!/usr/bin/perl -w + # Strict use strict; use warnings; + + # User-defined my $total = 10_000; + + # Main program my $i; print "Progress (of whatever)\n"; my $meter = new meter($total); for (my $i = 0; $i < $total; $i++) { $meter->update($i); # Call your code here &my_subroutine(); } $meter->update(); print "Done!\n"; + + # Subroutines sub my_subroutine() { # For test purposes, sleep for a random interval of milliseconds my $nmilli = int(rand(1010) / 1000); select(undef, undef, undef, $nmilli); } + + package meter; + # Libraries use Carp; + + # # Inputs: $1 ... the meter object # $2 ... total value (when reached, the meter will register + 100%) # # Description: Creates a "visual" meter, which displays an ascii pic +ture # representing the progress towards a total count. # sub new() { my ($proto, $total) = @_; ($total || 0) or croak "Invalid 'total' in object 'meter'"; + # User-configurable my $width = 64; # Total meter width my $blank = '-'; # Blank symbol my $done = '*'; # Progress symbol + my $interval = int($total / $width); my $class = ref($proto) || $proto; my $self = { 'total' => $total, 'interval' => $interval, 'width' => $width, 'blank' => $blank, 'done' => $done, }; bless $self, $class; $self->update(0); return $self; } + # # Inputs: $1 ... the meter object # $2 ... (optional) the progress count (if undefined, sets +the # count to the total, and outputs the final newline) +. # # Description: Updates the meter using the given count (which should + be # a minimum of zero, and a maximum of $this->{'total'}) +. # sub update() { my ($this, $count) = @_; my $done = defined($count)? 0: 1; my $total = $this->{'total'}; defined($count) or $count = $total; return unless ($count == $total || 0 == ($count % $this->{'interva +l'})); my $pct = 100 * $count / $total; my $width = $this->{'width'}; my $size = int($width * $count / $total); my $str0 = $this->{'done'} x $size; my $str1 = $this->{'blank'} x ($width - $size); select((select(STDERR), $| = 1)[0]); printf STDERR " %6.2f%% [%s%s]\r", $pct, $str0, $str1; $done and print STDERR "\n"; } + 1;
Re: printing a running count
by radiantmatrix (Parson) on Sep 16, 2005 at 16:12 UTC

    I'm assuming you don't know the final count of operations? If you do, check out Term::ProgressBar, which will even give you an ETA. ;-)

    Otherwise:

    ## calculate Fibonacci sequence, only print status my @fib = (0,1); my $rpt_freq = 1000; # how many elements until I print a mark? while (@fib < 50000) { # only do first 50k numbers push @fib, $fib[-1]+$fib[-2]; } continue { print '...'.scalar(@fib) if ( @fib % $rpt_freq == 0 ); }
    <-radiant.matrix->
    Larry Wall is Yoda: there is no try{} (ok, except in Perl6; way to ruin a joke, Larry! ;P)
    The Code that can be seen is not the true Code
    "In any sufficiently large group of people, most are idiots" - Kaa's Law