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

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

Hello.

What I really want (if the code worked as I was expecting):

sub create_f { my $func = shift; return sub { my $var = "xpto"; &$func; } } my $function = create_f( sub { print ">> $var <<\n" } );

I mean, supply a function that uses a variable that is declared outside. Of course I do not want this on this context, this is just my test code.

I do not want to make the variable global to the package.

Any hints?

Thanks! :)

Alberto Simões

Replies are listed 'Best First'.
Re: Making variables visible between calls.
by kennethk (Abbot) on Dec 12, 2008 at 18:24 UTC

    So you want the behavior of a global variable without a global variable? Are you trying to limit access to the value, or are you just trying to create persistent parameters? In either case, sounds like you want to go OOP-esque.

    package Parameters; use strict; use Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(get_var); my $var = "xpto"; sub get_var{ return $var; } 1;

    and then call it as

    use Parameters; print get_var;

    This has the added benefit that $var is completely inaccessible outside of any accessor methods you give the user.

Re: Making variables visible between calls.
by ikegami (Patriarch) on Dec 12, 2008 at 18:09 UTC
    sub create_f { my $func = shift; return sub { $func->("xpto"); } } my $function = create_f( sub { my $var = shift; print ">> $var <<\n" } );

    It's technically possible using dynamic scoping (local), but that increases coupling. What I presented is a much better alternative.

      Yes, at the moment that is my own solution, but that forces the user to get a set of predefined parameters. It works, but not as clean as I would like it to be to the user. ;)

      Thanks

      Alberto Simões

        A set? No. You could pass a hash or an object.

        Not as clean? Any method of passing arguments without using parameters would be less clean than using parameters.

Re: Making variables visible between calls.
by kyle (Abbot) on Dec 12, 2008 at 18:15 UTC

    Allow the function to take an argument.

    sub create_f { my $func = shift; return sub { my $var = 'xpto'; $func->( $var ); } } my $f = create_f( sub { my $var = shift; print ">> $var <<\n"; } );

    Otherwise, I don't see a way to do it. The sub can't reach outside its scope for variables that aren't global.

    You could cook up something with eval, maybe.

    sub create_f { my $func = shift; return sub { my $var = "xpto"; eval $func; } } my $function = create_f( 'print ">> $var <<\n"' ); $function->(); __END__ >> xpto <<

    I'm not sure if that fits with your real problem or not.

Re: Making variables visible between calls.
by Arunbear (Prior) on Dec 12, 2008 at 18:31 UTC
    You could put the variables into some other package which exists just to hold them e.g.
    #!/usr/bin/perl use strict; use warnings; sub create_f { my $func = shift; return sub { $MyBucket::var = "xpto"; &$func; } } my $function = create_f( sub { print ">> $MyBucket::var <<\n" } ); $function->();
Re: Making variables visible between calls.
by friedo (Prior) on Dec 12, 2008 at 18:12 UTC

    Your anonymous subroutine can't see $var since it's compiled in a lexical scope separate from where it's executed. But it can have values passed into it as arguments.

    sub create_f { my $func = shift; return sub { my $var = "xpto"; $func->( $var ); } } my $function = create_f( sub { print ">> $_[0] <<\n" } ); $function->();

    Update: And if you want to specify the value at runtime, it's as easy as passing it into create_f.

    sub create_f { my $func = shift; my $var = shift; return sub { $func->( $var ); } } my $function = create_f( sub { print ">> $_[0] <<\n" }, 'xpto' ); $function->();

Re: Making variables visible between calls.
by jeffa (Bishop) on Dec 12, 2008 at 18:06 UTC

    How about just this?

    my $function = sub { my $var = 'xpto'; print ">> $var <<\n" }; $function->();

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      That doesn't help. I need to let the user define his own function, and give him the possibility to access some pre-defined variables in case he wish to.

      My only possibility at the moment is to force the user defined function to receive a set of predefined parameters. But that will mess up some things later...

      But thanks, anyway ;)

      Alberto Simões

        It sounds like you realy want to pass it a hash. Then just preload the hash with values that it should have access to.

        my $function = sub {my $v = shift; print ">> $v->{var} <<\n" }; my $params = {var => 'exto'}; $function->($params);

        ___________
        Eric Hodges