http://www.perlmonks.org?node_id=1006618

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

Update:

I've tried to figure out a simplified example:

Update: Edited wrong node!

#!/usr/bin/perl use IO::All; use strict; use warnings; use Getopt::Long; use Pod::Usage; my ( %options, $option, $result ); # Getopt::Long::Configure("no_ignore_case"); GetOptions( \%options, "help", "suffix=s", "amount=i", "directory=s", +); pod2usage( -exitstatus => 0, -verbose => 2 ) if $options{help}; # adde +d foreach $option ( $options{dir}, $options{suffix}, $options{amount} ) +{ pod2usage( -exitstatus => 2, -verbose => 2 ) unless $option; } $result = find( $options{dir}, $options{suffix}, $options{amount} ); if ( $result != 0 ) { print qq(Found $result file(s)!\n); } else { print qq(Nothing found!\n); } sub find { my ( $dir, $time, $result, $suffix, $amount ); ( $dir, $suffix, $amount ) = @_; $time = time(); $amount = $amount * 60 * 60; $result = grep { $_ == 1 } map { $time - $_ >= $amount } map { ( $_->stat )[9] } # e.g i'm interested in this... grep { $_->name =~ /.+\.$suffix$/ } # ...and this io($dir)->all; return $result; } __END__ =pod =head1 NAME myFind.pl =head1 SYNOPSIS myFind.pl [options] =head1 OPTIONS =over 8 =item B<-h, --help> Prints a brief help message and exits. =item B<-a, --amount> Search for files older than <amount> hours, mandatory. =item B<-d, --directory> Directory to search in, mandatory. =item B<-s, --suffix> + File suffix, mandatory. =back =head1 DESCRIPTION It's just a finger exercise. =head1 USAGE ./myFind.pl -a 1 -d . -s pl =head1 AUTHOR Karl Goethebier <karl.goethebier@gmx.de> =cut

Thanks and regards, Karl

«The Crux of the Biscuit is the Apostrophe»

Replies are listed 'Best First'.
Re: How can i debug compound map/grep statements just using print?
by LanX (Saint) on Dec 01, 2012 at 18:22 UTC
    please, next time post clear questions with isolated code examples.

    map and grep use code blocks which depend on the return value of the last statement.

    that means you can use the same techniques used for debugging subroutines, e.g. you can use print anywhere within these blocks, as long as it doesn't change the returned value.

    you can also split deeply nested maps and greps in smaller chunks filling temporary arrays and dump these results with Data::Dumper.

    UPDATE:

    and if you really need it very often, use something which assures the same list as in- and output

    sub dumplist { use Data::Dumper; print Dumper \@_; return @_; } @result= map { ... } dumplist grep { ... } @input;

    example:

    @evenchars = map { $_->[0] } dumplist grep { $_->[1] % 2 } map { [ $_, ord($_) ] } a..l; #prints $VAR1 = [ [ 'a', 97 ], [ 'c', 99 ], [ 'e', 101 ], [ 'g', 103 ], [ 'i', 105 ], [ 'k', 107 ] ];

    Cheers Rolf

      You answered: "please, next time post clear questions with isolated code examples.

      My question was: "How can i debug compound map/grep statements just using print?"...and in my code you can read this:

      map { ( $_->stat )[9] } # e.g i'm interested in this... grep { $_->name =~ /.+\.$suffix$/ } # ...and this

      How clear can we get ;-)

      You answered: "you can use print anywhere within these blocks as long as it doesn't change the returned value."

      This is what i was searching for! Honestly said: i didn't know this. And perhaps i've got yet another mental block. Thank you very much for this advice.

      Best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

        Well you posted more than 10 times more code than necessary.

        I said "with isolated code examples." =)

        Take your new terse explanation as a standard for your coming posts. ;)

        Cheers Rolf

        A reply falls below the community's threshold of quality. You may see it by logging in.

        Your response did get a smile out of me, but the only part of the code that was necessary to show your problem is the find subroutine. All the POD and getoptions code you added are no doubt necessary for a complete program, but not to show the problem you are having

        A Monk aims to give answers to those who have none, and to learn from those who know more.
Re: How can i debug compound map/grep statements just using print?
by roboticus (Chancellor) on Dec 01, 2012 at 19:04 UTC

    karlgoethebler:

    Before I resort to using print statements, I try to read the statement bit by bit to see if I'm going to get what I want.

    $result = grep { $_ == 1 } ...a list...

    Ok, so grep is going to read a list and keep the elements equal to 1. We're assigning a list to a scalar, so $result is ultimately going to be the number of things that are equal to 1. Is that what you're looking for? Judging by your code, that seems to be the case, so far so good.

    That bit of code is receiving a list of values from comparisons:

    ..new list.. = map { $time - $_ >= $amount } ..a list..

    which will return a list of 1 and '', that looks good, too.

    Eventually, I either find the bug, or I just don't see it. So then I resort to my next technique. I alter the code a bit, to look like this:

    my @t = #$result = # grep { $_ == 1 } # map { $time - $_ >= $amount } # map { ( $_->stat )[9] } # e.g i'm interested in this... # grep { $_->name =~ /.+\.$suffix$/ } # ...and this io($dir)->all; die "<", join(", ", @t), ">\n";

    Now when I run it, I can see the list we're starting from. If it looks fine, then I uncomment the last commented line, and try again:

    my @t = #$result = # grep { $_ == 1 } # map { $time - $_ >= $amount } # map { ( $_->stat )[9] } # e.g i'm interested in this... grep { $_->name =~ /.+\.$suffix$/ } # ...and this io($dir)->all; die "<", join(", ", @t), ">\n";

    By working my way backwards through the filters, I can see where the list looks wrong, and make suitable fixes. Then, since I'm in this cycle anyway, I go ahead and complete the process, until I the list run through all filters. If it looks good, then I simply uncomment the assignment to $result, and delete the my @t = and die lines.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Yes, but my intent was more naive. I searched for a equivalent using map/grep for saying:

      for(@things) {print if DEBUG}

      «The Crux of the Biscuit is the Apostrophe»

Re: How can i debug compound map/grep statements just using print?
by tobyink (Canon) on Dec 01, 2012 at 18:34 UTC
    use strict; use warnings; { my $level = 0; sub reset_list_debug { $level = 0 } sub custom_list_debug (&@) { my $code = shift; print "==" x $level, $level ? "> " : '', join(q( ), map($code- +>($_), @_)), "\n"; $level++; return @_; } sub list_debug { custom_list_debug { "$_" } @_; } } my @letters = list_debug map { lc $_ } list_debug grep { /[AEIOU]/ } list_debug map { uc $_ } list_debug 'a'..'z'; reset_list_debug(); my @letters2 = custom_list_debug { qq("$_") } map { lc $_ } custom_list_debug { qq([$_]) } grep { /[AEIOU]/ } custom_list_debug { qq('$_') } map { uc $_ } custom_list_debug { qq( $_ ) } 'a'..'z'; reset_list_debug();
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      I tried this:

      Karls-Mac-mini:find karl$ ./myFind.pl -a 1 -d . -s pl ./bar.pl ./benchmark.pdf ./foo.pl ./leftover.pl ./myFind.pl ./tobyink. +pl ==> ./bar.pl ./foo.pl ./leftover.pl ./myFind.pl ./tobyink.pl ====> 1354402483 1354402472 1354381750 1354402315 1354398126 ======> 1 1 ========> 1 1 Found 2 file(s)! Karls-Mac-mini:find karl$ ls -hlrt total 1408 -rw-r--r--@ 1 karl karl 684K 10 Nov 11:32 benchmark.pdf -rw-r--r-- 1 karl karl 58B 1 Dez 18:09 leftover.pl -rw-r--r--@ 1 karl karl 689B 1 Dez 22:42 tobyink.pl -rwxr-xr-x@ 1 karl karl 1,4K 1 Dez 23:51 myFind.pl -rw-r--r-- 1 karl karl 5B 1 Dez 23:54 foo.pl -rw-r--r-- 1 karl karl 5B 1 Dez 23:54 bar.pl

      Update: Thank you very much! Regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

Re: How can i debug compound map/grep statements just using print?
by Kenosis (Priest) on Dec 01, 2012 at 19:01 UTC

    Did you mean:

    $amount = $amount * 24 * 60 * 60;

    And have you tried:

    $result = grep { /.+\.$suffix$/ and $time - ( $_->stat )[9] >= $amount } io($dir)->all;

    $result is the file count...

      No.

      Karls-Mac-mini:~ karl $ perl -e 'print 2*60*60;' 7200
      perl -e 'print scalar( grep { $_ == 1 } (0,0,0) );' # nothing found 0
      perl -e 'print scalar( grep { $_ == 1 } (1,0,0) );' # one found 1

      «The Crux of the Biscuit is the Apostrophe»

        You have the following:

        =item B<-a, --amount> Search for files older than <amount> days, mandatory.

        In two days, there are 2 * 24 * 60 * 60 or 172800 seconds, thus it seemed that the following was needed:

        $amount = $amount * 24 * 60 * 60;

        since one day equals 24 * 60 * 60 or 86400 seconds. Additionally, the following worked when I ran it using the above formula for $amount:

        $result = grep { /.+\.$suffix$/ and $time - ( $_->stat )[9] >= $amount } io($dir)->all;

        An equivalent search can be done using File::Find::Rule:

        $result = File::Find::Rule->file() ->maxdepth(1) ->name(qr/.+\.$suffix$/i) ->mtime(">= $amount") ->in($dir);

        And the following, too, but grepping on days old instead of using mtime:

        $result = grep { -M >= $amount / 86400 } # Convert back to days File::Find::Rule->file() ->maxdepth(1) ->name(qr/.+\.$suffix$/i) ->in($dir);

        All three methods produced the same result, even when an equivalent constant was used for the days in the last grep.

        After adjusting the $amount formula, your original code produced the same result, too:

        $result = grep { $_ == 1 } map { $time - $_ >= $amount } map { ( $_->stat )[9] } # e.g i'm interested in this... grep { $_->name =~ /.+\.$suffix$/ } # ...and this io($dir)->all;

        Thus, all four code snippets produced identical results.