views:

143

answers:

4

I am doing pass-by-reference like this:

use strict;
use warnings;

sub repl {
    local *line = \$_[0]; our $line;
    $line = "new value";
}

sub doRepl {
    my ($replFunc) = @_;
    my $foo = "old value";
    $replFunc->($foo);
    print $foo; # prints "new value";
}

doRepl(\&repl);

Is there a cleaner way of doing it?

Prototypes don't work because I'm using a function reference (trust me that there's a good reason for using a function reference).

I also don't want to use $_[0] everywhere in repl because it's ugly.

+3  A: 

There are a couple of ways to do this. Explicitly pass a scalar ref to $foo, or take advantage of Perl's built-in pass by reference semantics.

Explicit reference:

my $foo = "old value";
doRepl( \&repl, \$foo );
print $foo; # prints "new value";

sub repl {
    my $line = shift;
    $$line = "new value";
}

sub doRepl {
    my ($replFunc, $foo) = @_;
    $replFunc->($foo);
}

Pass by reference:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    $replFunc->(@_);
}

Even fancier pass by reference:

my $foo = "old value";
doRepl( \&repl, $foo );
print $foo; # prints "new value";

sub repl {
    $_[0] = "new value";
}

sub doRepl {
    my $replFunc = shift;
    &$replFunc;
}

The first one use normal perl hard references to do the job.

The first pass by ref method uses the fact that Perl passes arguments to all functions as references. The elements of @_ are actually aliases to the values in the argument list when the subroutine is called. By altering $_[0] in foo(), you actually alter the first argument to foo().

The second pass by ref method use the fact that a sub called with an & sigil and no parens gets the @_ array of its caller. Otherwise it is identical.

Update: I just noticed you desire to avoid $_[0]. You can do this in repl if you want:

sub repl {
    for my $line( $_[0] ) {
        $line = 'new value';
    }
}
daotoad
thanks... good info, but I want "implicit" pass-by-ref
JoelFan
+9  A: 

Have you looked at Data::Alias? It lets you create lexically-scoped aliases with a clean syntax.

You can use it to create pass-by-reference semantics like this:

use strict;
use warnings;

use Data::Alias;

sub foo {
    alias my ($arg) = @_;
    $arg++;
}

my $count = 0;

foo($count);

print "$count\n";

The output is 1, indicating that the call to foo modified its argument.

Philip Potter
Unfortunately, I looked at the "Implementation" section of the docs... ack! I guess this module is kind of like sausages... much more enjoyable if you don't know how they're made... :)
JoelFan
@JoelFan: yup. This is very much a "Don't try this at home" module :)
Philip Potter
starts out "This module does not use a source filter" and just goes downhill from there... :)
JoelFan
I don't care how it works. It's awesome.
Ryan Thompson
+3  A: 

 

sub repl {
    my $line = \$_[0];     # or: my $line = \shift
    $$line = "new value";
}
mobrule
or `my ($line) = \ (@_);`
Sinan Ünür
@Sinan - wouldn't have guessed that would work. Learning something new every day.
mobrule
@mobrule see http://perldoc.perl.org/perlref.html#Making-References "As a special case, `\(@foo)` returns a list of references to the contents of `@foo`, not a reference to `@foo` itself."
Sinan Ünür
In scalar context, `\(@foo)` is a reference to a scalar value that contains the number of elements in `@foo` [i.e., like `\(scalar @foo)`]
mobrule
A: 

I don't think there is anything wrong with using local to create the alias in this case.

Dynamic scope is of course a powerful feature, but so long as you are aware of the side effects (new value is visible in functions called from its scope, if a lexical of the same name is in scope, it can't be localized, ...) then it is a useful addition to the already overflowing Perl toolbox.

The main reason for the warnings in the Perl docs about local are to keep people from inadvertently using it instead of my and to ease the transition from perl4. But there are definitely times when local is useful, and this is one.

Using for to create your alias is also an option, but I find the explicit syntax with local clearer in its intent. It is also a bit faster if performance is a concern.

Eric Strom