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

Below I have got while loop which check some conditions and It works. I am looking forward if any one can suggest me how to make it more compact. Any suggestions?Please.
while (<$fh>) { if (/^M/) { my @output = split('/'); my $filename = $output[-1]; chomp($filename); if ($filename eq 'people.pdf') { $people = "1" }; if ($filename eq 'animal.pdf') { $animal = "1" }; if ($filename eq 'setup.msi') { $msi = "1" }; } } if (!(defined $people)) { print "People not found\n"; } if (!(defined $animal)) { print "animal not found\n"; } if (!(defined $msi)) { print "MSI file not found\n"; } exit;
Cheers

Replies are listed 'Best First'.
Re: Perlish while loop
by CountZero (Bishop) on Jul 16, 2009 at 06:21 UTC
    You could consider using File::Basename to extract the filename.
    use File::Basename; # ... while (<$fh>) { chomp; if (/^M/) { my $filename = fileparse($_); # ... } }
    It has as an additional benefit that it takes care of the different file-path separators on Unix, Windows, Mac, VMS, ..., making your script more compatible with different OSs.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Perlish while loop
by rovf (Priest) on Jul 16, 2009 at 08:33 UTC

    If you know you will stick with these three files (people, animal, msi), it's OK; but if you think that maybe the files to check might change, or will become more, you might want to set up a table of valid filenames,

    my %filenames = ( 'people.pdf' => 'People', 'setup.msi' => 'MSI', # etc. )
    In your loop, you would check whether $filename occurs in %keys %filenames, and if it does, removes it from %filenames. After the loop, you just have to print values %filenames to list the names of files which haven't been found. This will be a lot easier to maintain.

    -- 
    Ronald Fischer <ynnor@mm.st>
Re: Perlish while loop
by GrandFather (Saint) on Jul 16, 2009 at 10:53 UTC

    There are many ways to refactor that code to make it easier to maintain. If the work to be done is really as light weight as is indicated then I'd tend toward a light weight object approach:

    use strict; use warnings; my $obj = bless {}; $obj->processLine ($_) while <DATA>; $obj->report (); sub processLine { my ($self, $line) = @_; return if $line !~ /^M/; chomp $line; my $filename = (split '/', $line)[-1]; return if ! defined $filename; $self->{files}{$filename} ||= $line; } sub report{ my ($self) = @_; print "People not found\n" if ! exists $self->{files}{'people.pdf' +}; print "animal not found\n" if ! exists $self->{files}{'animal.pdf' +}; print "MSI not found\n" if ! exists $self->{files}{'setup.msi'}; } __DATA__ M/people.pdf M/wiggles.pdf M/setup.msi

    Prints:

    animal not found

    which scales nicely as you need to detect more or fewer file names, but could easily be changed to use a dispatch table in the 'report' section to handle more work for each found file.


    True laziness is hard work
Re: Perlish while loop
by dsheroh (Monsignor) on Jul 16, 2009 at 11:42 UTC
    Perl allows postfix flow control, which would tighten this up a bit. Personally, I also find this version much more readable, but there are those who would disagree:
    ... $people = 1 if ($filename eq 'people.pdf'); $animal = 1 if ($filename eq 'animal.pdf'); $msi = 1 if ($filename eq 'setup.msi'); } } print "People not found\n" unless defined $people; print "animal not found\n" unless defined $animal; print "MSI file not found\n" unless defined $msi; ...
    rovf's suggestion with using a hash to store the mapping of filenames to file descriptions is definitely the way to go for longer-term maintenance, since it allows you to expand the range of things you're looking for by simply adding a new hash entry, turning this into:
    my %filenames = ( people.pdf => 'Person', animal.pdf => 'animal', setup.msi => 'MSI file', ); ... for (keys %filenames) { delete $filenames{$_} if $filename eq $_; } } } for (values %filenames) { print "$_ not found\n"; } ...

      ESPer, why did you go with

      for (keys %filenames) { delete $filenames{$_} if $filename eq $_; }

      instead of simply

      delete $filenames{$filename};

      and just not worry about the cases where the $filename wasn't one of the ones that you're interested in? keys has to build a list, then that has to be traversed, and each value compared. I could understand

      if ( exists $filenames{$filename} ) { delete $filenames{$filename}; }

      especially if there is a chance that %filenames is tied to something big and slow. Is there something that I overlooked?

      - doug

        Honestly? Because I was translating from the non-hash version above it and just copied the structure without thinking about it. Thanks for catching that!
Re: Perlish while loop
by Anonymous Monk on Jul 16, 2009 at 06:17 UTC
    You shouldn't make it more compact than this; enjoy!
    while (<$fh>) { if (/^M/) { my @output = split('/'); my $filename = $output[-1]; chomp($filename); if ( $filename eq 'people.pdf' ) { $people = "1"; } if ( $filename eq 'animal.pdf' ) { $animal = "1"; } if ( $filename eq 'setup.msi' ) { $msi = "1"; } } } if ( !( defined $people ) ) { print "People not found\n"; } if ( !( defined $animal ) ) { print "animal not found\n"; } if ( !( defined $msi ) ) { print "MSI file not found\n"; } exit;
Re: Perlish while loop
by spazm (Monk) on Jul 16, 2009 at 07:25 UTC
    a bit tighter. Basename is probably better than the s,.*/,, as pointed out above.
    while (<$fh>) { if (/^M/) #What is this ? { chomp; my $filename = $_; $filename s,.*/,,; #strip off leading path info $seen->{$filename}++ if( $filename =~ /pdf/ || $filename =~ /msi/) } } if (! exists $seen->{'people.pdf'} ) { print "People not found\n"; } if (! exists $seen->{'animal.pdf'} ) { print "animal not found\n"; } if (! exists $seen->{'setup.msi')) { print "MSI file not found\n"; } exit;
      Your Solution is another new thing for me but I am bit paranoid about using "$seen", as looking at your code you are using $seen as a hash. But while declaring if I define "seen" as
      my %seen
      That give me error but if I declare it as a $seen variable it works and that makes me confuse. Any Suggestion?Please. Cheers
Re: Perlish while loop
by doug (Pilgrim) on Jul 16, 2009 at 19:35 UTC

    how about

    use strict; use warnings; my %saw = (); while ( <DATA> ) { next unless ( m#^M.*/([^/]+)# ); my $filename = $1; chomp $filename; $saw{$filename} = 1; } print "People not found\n." unless ( $saw{'people.pdf'} ); print "animal not found\n" unless ( $saw{'animal.pdf'} ); print "MSI file not found\n" unless ( $saw{'setup.msi'} ); __DATA__ M/people.pdf M/wiggles.pdf M/setup.msi

    You do twice as many "if"s as necessary: just store everything and do the checks once.

    - doug