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

Dear Monks,

I have a couple of questions about the usage of File::Next and some basic anonymous subroutine concepts I don't understand. Here's what works for me so far.

use strict; use warnings; use File::Next; my $find_this = sub { my ($starting_points, $target) = @_; my $ref_sub = File::Next::files( "$starting_points" ); while ( my @files = $ref_sub->() ) { return @files if $files[1] =~ $target; } }; my @files = &$find_this( "/tmp/dir1", "file3.txt" ); print "@files\n";

__output__

$ ./file_next.pl /tmp/dir1/dir3 file3.txt /tmp/dir1/dir3/file3.txt

My questions:

The docs for File::Next state that the first parameter to any of the iterator factory functions may be a hashref of options and gives an example like this:

For the three iterators, the \%options are optional. files( [ \%options, ] @starting_points )])

I understand they're optional but what are examples of something I would use there for \%options?

I think I only half understand what this line is doing:
while ( my @files = $ref_sub->()  ) {
What I see is the reference $ref_sub being assigned to @files for each file down the directory tree and while that condition is true do something. This works fine but am I supposed to put something in the parens after $ref_sub->(HERE) ?

Is there a way that I can put my anonymous subroutines down in the bottom of my file out of the way like I do with regular subroutines and then put the line where I call it somewhere higher above?

Thank-you again for any advice.

UPDATE:
I finally realized I didn't need to make everything anonymous subroutines to avoid getting "Variable $x will not stay shared" errors and instead I could have subroutines within subroutines by passing data and return values into and out of the subroutines keeping the data local to the subroutine instead of making it global.
FWIW about the File::Next question, here's what I ended up using

sub findfile { my ($target, $starting_points ) = @_; my $file = File::Next::files( { file_filter => sub { /$target$ +/ } }, "$starting_points" ); while ( my @files = $file->() ) { return $files[0], $files[1], $files[2]; #direc +tory, filename, full path and name } }
Thanks for the feedback that you provided on this. It was very useful.

Replies are listed 'Best First'.
Re: What are valid File::Next \%options and anonymous subroutine placement in script
by ikegami (Patriarch) on Mar 04, 2009 at 03:00 UTC

    What I see is the reference $ref_sub being assigned to @files

    No, the result of calling the referenced code is assigned.

    @files is a misnomer. "In list context, the iterator returns a list containing $dir, $file and $fullpath", the directory in which the next file is located, the name of the next file and the path to the next file.

    am I supposed to put something in the parens after $ref_sub->(HERE) ?

    No. The iterator has no parameters.

    Is there a way that I can put my anonymous subroutines down in the bottom of my file

    Given there's no reason for it to be anonymous in your case...

    what are examples of something I would use there for \%options?

    They're listed under "CONSTRUCTOR PARAMETERS".

Re: What are valid File::Next \%options and anonymous subroutine placement in script
by ELISHEVA (Prior) on Mar 04, 2009 at 04:20 UTC
    Is there a way that I can put my anonymous subroutines down in the bottom of my file out of the way like I do with regular subroutines and then put the line where I call it somewhere higher above?

    Yes and no.

    • No - anonymous subroutine must either be defined directly in the parameter list or else assigned to a variable which is passed as a parameter. Variables have to be assigned before they are used, if you want them to have a value other than undef
    • Yes - you don't have to use anonymous subroutines to pass in a subroutine. You can also pass the address of a named subroutine as a parameter. To get the address, prefix the subroutine name with \& and pass it like this foo(\&someNamedSub). You can also assign the address to a variable and pass it like this: my $x=\&someNamedSub; foo($x);. And, of course, you can define named subs below the place where you call them.
    • Yes - if you want to pass a closure instead of a plain old named sub, but you still want the closure generation process "down below", you can define a named generator function below. When you need the closure you call the generator and pass its result, like this:
      foo(make_a_closure(".xml")); #or my $x=make_a_closure(".xml"); foo($x); sub make_a_closure { my ($sExt) = @_; return sub { ...#do something with $sExt } }

    Best, beth