Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to pass by reference in Perl?

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.

like image 410
JoelFan Avatar asked Mar 17 '10 18:03

JoelFan


3 Answers

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.

like image 183
Philip Potter Avatar answered Sep 22 '22 23:09

Philip Potter


 

sub repl {
    my $line = \$_[0];     # or: my $line = \shift
    $$line = "new value";
}
like image 39
mob Avatar answered Sep 20 '22 23:09

mob


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';
    }
}
like image 40
daotoad Avatar answered Sep 24 '22 23:09

daotoad