Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re: aborting File::Find::find

by Sidhekin (Priest)
on Nov 16, 2006 at 18:37 UTC ( [id://584559]=note: print w/replies, xml ) Need Help??


in reply to aborting File::Find::find

In situations like these, I've been arguing for goto LABEL:

find( sub { ...; --$fnlinks or goto DONE; }, $dir ); DONE: ...

It has twothree advantages over the eval/die pair:

  • it makes your intention more clear;

  • it is better prepared for future versions of File::Find, that just might treat die-ing callbacks differently (that the eval/die pair works does not follow from the documentation); and

  • as Fletch++ notes, eval/die is prone to hiding errors as other code dies. goto LABEL has no corresponding weakness.

The last LABEL version is similar, but noisy under -w or use warnings; generating Exiting subroutine via last warnings.

Besides, goto LABEL is more readable still.

print "Just another Perl ${\(trickster and hacker)},"
The Sidhekin proves Sidhe did it!

Replies are listed 'Best First'.
Re^2: aborting File::Find::find (gotchas)
by tye (Sage) on Nov 16, 2006 at 22:19 UTC

    Did you:

    grep DONE: File/Find.pm

    before you wrote this code? Do you do that for every label you have ever used this way each time you upgrade File::Find? I'd have more sympathy for going against the first paragraph of "perldoc -f goto"1 if Perl supported the following syntax:

    File::Find::find( sub { ... goto My::Package::FOUND ... } ... ); My::Package::FOUND: ....

    Does that give you a better feel why some people are not in favor of such a practice?

    I suspect that using last to exit a sub generates a warning because it is widely considered a bad practice and last is used enough that accidentally using it to exit a subroutine appeared on the radar. So the warning for last and not for goto has more to do with last being seen as useful and used while goto is not. In particular, the lack of a warning is not an indication that the practice is not seen as a bad one (sorry, I wanted one more negation in there but I grew tired).

    I shy aware from using things that the documentation tells me that I shouldn't. Those are the things that get "fixed" to no longer work or that aren't well tested and so nobody notices when they break and so bugs in them survive into "stable" releases.

    I shy aware from practices that break just because someone else somewhere also came up with "DONE" for their label name.

    But I do see the appeal of such simple and clear code (once the reader gets over the shock of using goto to jump out of a possibly large number of subroutine calls, that is).

    BTW, my solution for this problem would probably be to spend less time rolling a dozen-line replacement for File::Find then I'd have to spend searching the File::Find docs to see if it supported a "thanks, I'm done" feature. The classic gotchas are well known to me2 (as well as the failings of File::Find that have caused me grief repeatedly over the many years when I've been unfortunate enough to try to use it or try to use something that used it).

    Here, an example I threw together in a couple of minutes:

    BEGIN { my( $dot, $up )= ( File::Spec->curdir(), File::Spec->updir() ); sub SearchDirs { my( $path, $subdir )= @_; $path= File::Spec->catdir( $path, $subdir ); chdir $subdir or die "Can't chdir, $path: $!\n"; opendir D, $dot or die "Can't opendir, $path: $!\n"; my @files= grep $_ ne $dot && $_ ne $up, readdir D; closedir D; for( @files ) { my $recurse= ! -l $_ && -d _; # ... SearchDirs( $path, $_ ) if $recurse; } chdir $up or die "Can't chdir .. from $path: $!\n"; } }

    Using the above would mean that I at least wouldn't have to worry about "goto DONE;" suddenly not working because I upgraded File::Find.

    Just for fun, let's avoid that whole problem and make return work by eliminating the recursion:

    #!/usr/bin/perl -w use strict; use File::Spec; # Globals: my( $FiNode, $FnLinks, @Found ); Main( @ARGV ); exit( 0 ); BEGIN { my( $dot, $up )= ( File::Spec->curdir(), File::Spec->updir() ); sub SearchDirs { my( $path )= @_; my @depth= "."; my @todo= ( ".", $path ); while( @todo ) { my $path= shift @todo; my $subdir= shift @todo; while( $path ne $depth[-1] ) { chdir $up or die "Can't chdir .. from $depth[-1]: $ +!\n"; pop @depth; } $path= File::Spec->catdir( $path, $subdir ); push @depth, $path; chdir $subdir or die "Can't chdir, $path: $!\n"; opendir D, $dot or die "Can't opendir, $path: $!\n"; my @files= grep $_ ne $dot && $_ ne $up, readdir D; closedir D; for( @files ) { push @todo, $path, $_ if ! -l $_ && -d _; if( $FiNode == ( lstat _ )[1] ) { push @Found, File::Spec->catfile( $path, $_ ); return if --$FnLinks < 1; } } } } } sub Main { die "Usage: file dir\n" if 2 != @_; my( $fpath, $dir )= @_; ( $FiNode, $FnLinks )= ( lstat $fpath )[1,3]; if( 1 == $FnLinks ) { print $fpath, $/; } else { SearchDirs( $dir ); print $_, $/ for @Found; } }

    It even works. (:

    - tye        

    1 Quote: It can be used to go almost anywhere else within the dynamic scope, including out of subroutines, but it's usually better to use some other construct such as "last" or "die".

    2 Such as:

    1. Either use chdir and opendir on File::Spec->curdir() or prepend the path before using stat-like operations
    2. Skip File::Spec->curdir() and ->updir() or you'll loop forever
    3. Don't recurse into symbolic links (or keep a hash of where you've been or just assert that circular links cause infinite loops)

Re^2: aborting File::Find::find
by Limbic~Region (Chancellor) on Nov 16, 2006 at 18:54 UTC
    Sidhekin,
    In this case, I will have to flat out disagree. The sane solution is to patch File::Find. See my comment to jdporter above. Could you remind me what module of your's uses goto label. I promised some time ago that I would look at it to try and see if there was a sane alternative and never did. I do remember at first glance it was a pretty tough cookie to crack.

    Cheers - L~R

      Well, I'll agree that one sane solution would be to patch File::Find. Provided you have the time, and the patch is accepted, that is. Meawhile, though we may disagree, I maintain that goto LABEL is another. :-)

      Test::Trap uses goto LABEL. (It's the second goto in the source; the first is a goto &function.)

      Tough? Since it calls into user code that may contain arbitrary many levels of subroutine calls and/or eval, I see no way to do it without a LABEL, and I'll argue that goto LABEL is more readable than last LABEL where, as here, the latter would require the addition of a loop-once block and a no warnings 'exiting'.

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

        Sidhekin,
        I will look into Test::Trap as promised.

        ... and I'll argue that goto LABEL is more readable than last LABEL where, as here, the latter would require the addition of a loop-once block and a no warnings 'exiting'.

        I just want to make it clear for those following along at home that your comment applies to the label solution proposed by jdporter and not my proposed patching of File::Find which would work cleanly.

        Cheers - L~R

Re^2: aborting File::Find::find
by brig (Scribe) on Nov 16, 2006 at 22:00 UTC

    IMHO goto is a quite exceptable solution when:

    1. It is easy to read and understand the intent
    2. It is within several lines (no more than say 12 lines) from the label

    YMMV

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://584559]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (6)
As of 2024-04-19 14:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found