Very basic question on using references inside functions

M

Mark Seger

I'd have thought this would have been answered in some of the
documentation on using references but if it was I missed it.

I understand that if I want to call a function and pass it a reference
to a scalar I can then modify that scalar within the function as follows:

sub foo {
my $ref=shift;
$$ref='new value';
}

and it works just fine. However, I want to use that scalar within the
function many times and don't want to call it $$ref every time because
it feels clumsy.

Is there some other way? I thought I could make a reference to that
reference by something like $var=\$$ref and just access $var but that
didn't work. My REAL motive for asking is I have some code that passes
parameters by value and I'd like to go back and pass them by reference
without have to change all the other code that touches them.

Then again, maybe the perlish way to do it IS to refer to them
everywhere as $$ref, $@ref or %$ref just to be clearer about what it
really going on.

-mark
 
A

Anno Siegel

Mark Seger said:
I'd have thought this would have been answered in some of the
documentation on using references but if it was I missed it.

I understand that if I want to call a function and pass it a reference
to a scalar I can then modify that scalar within the function as follows:

sub foo {
my $ref=shift;
$$ref='new value';
}

and it works just fine. However, I want to use that scalar within the
function many times and don't want to call it $$ref every time because
it feels clumsy.

It's how it's done, clumsy or not.
Is there some other way? I thought I could make a reference to that
reference by something like $var=\$$ref and just access $var but that
didn't work.

That would give you an additional layer of reference and make the
access more complicated, not less.
My REAL motive for asking is I have some code that passes
parameters by value and I'd like to go back and pass them by reference
without have to change all the other code that touches them.

Perl *does* pass scalars "by reference" in the sense that a parameter
value can be changed from within the function. Example:

sub foe {
$_[ 0] = 'new value';
}
my $x = 'old value';
fum( $x);
print "$x\n";

If you want to access the variable under a meaningful name (instead of
$_[ 0]) you can use a one-shot for loop to get an alias (this is also
called topicalization):

sub fum {
for my $variable ( $_[ 0] ) {
$variable = 'new value';
}
}

So references are not really necessary to change a scalar variable
from within a sub. (Aggregates are a different matter.) However,
a code change will be necessary either way.

Anno
 
G

Gunnar Hjalmarsson

Mark said:
I understand that if I want to call a function and pass it a reference
to a scalar I can then modify that scalar within the function as follows:

sub foo {
my $ref=shift;
$$ref='new value';
}

and it works just fine. However, I want to use that scalar within the
function many times and don't want to call it $$ref every time because
it feels clumsy.

My REAL motive for asking is I have some code that passes
parameters by value and I'd like to go back and pass them by reference
without have to change all the other code that touches them.

So you don't care whether the passed variable is changed? In that case,
there is of course the obvious dereference way:

sub foo {
my $var = ${ $_[0] };
$var = 'new value';
}

OTOH, then you wouldn't gain much from passing references instead of
values...
 
M

Mark Seger

Perl *does* pass scalars "by reference" in the sense that a parameter
value can be changed from within the function. Example:

sub foe {
$_[ 0] = 'new value';
}
my $x = 'old value';
fum( $x);
print "$x\n";
What I meant by 'pass by reference' is that any changes you make in the
function are reflected back at caller who will then see changes to that
parameter. I believe what perl does is 'pass by value', in that it
makes a copy of the data, thereby allowing you to change it without fear
of clobbering the orignal value.
If you want to access the variable under a meaningful name (instead of
$_[ 0]) you can use a one-shot for loop to get an alias (this is also
called topicalization):

sub fum {
for my $variable ( $_[ 0] ) {
$variable = 'new value';
}
}
I do this all the time for all variables, even in for loops as I find
references like @_ or $_ read very poorly, but that's me.

-mark
 
A

Anno Siegel

Mark Seger said:
Perl *does* pass scalars "by reference" in the sense that a parameter
value can be changed from within the function. Example:

sub foe {
$_[ 0] = 'new value';
}
my $x = 'old value';
fum( $x);
print "$x\n";
What I meant by 'pass by reference' is that any changes you make in the
function are reflected back at caller who will then see changes to that
parameter. I believe what perl does is 'pass by value', in that it
makes a copy of the data, thereby allowing you to change it without fear
of clobbering the orignal value.

You believe wrong. The code example above shows that changes to sub
arguments (the elements of @_) are indeed visible to the caller. The
"call-by-value" thing happens when you assign the content of @_ to
local variables, as in "my ( $x, $y, $z) = @_;". If you change $x etc.
in the sub body, nothing is changed in the environment. If you change
$_[ 0] etc. directly, it is.
If you want to access the variable under a meaningful name (instead of
$_[ 0]) you can use a one-shot for loop to get an alias (this is also
called topicalization):

sub fum {
for my $variable ( $_[ 0] ) {
$variable = 'new value';
}
}
I do this all the time for all variables, even in for loops as I find
references like @_ or $_ read very poorly, but that's me.

That's an unrelated point. The aliasing behavior of "for" is the same
whether you use an explicit variable or the default $_.

sub fum {
for ( $_[ 0] ) {
$_ = 'new value';
}
}

would have illustrated my point just as well.

Anno
 
X

xhoster

Mark Seger said:
I'd have thought this would have been answered in some of the
documentation on using references but if it was I missed it.

I understand that if I want to call a function and pass it a reference
to a scalar I can then modify that scalar within the function as follows:

sub foo {
my $ref=shift;
$$ref='new value';
}

and it works just fine. However, I want to use that scalar within the
function many times and don't want to call it $$ref every time because
it feels clumsy.

Tough it out. It stops feeling clumsy after a while.
Is there some other way? I thought I could make a reference to that
reference by something like $var=\$$ref and just access $var but that
didn't work.

That is effectively the same thing as $var=$ref; It doesn't change the way
you dereference it.

My REAL motive for asking is I have some code that passes
parameters by value and I'd like to go back and pass them by reference
without have to change all the other code that touches them.

If it currently passes parameters (effectively) by value, and it does the
right thing, then wouldn't changing it to pass them by reference cause it
to do the wrong thing unless you change all that other code anyway?

You could use typeglobs (or whatever it is I am supposed to call those
things). I don't particularly recommend it, but I have used it in a pinch.

use strict;

my $x='j' x 10;

print "$x\n";
foo3($x);
print "$x\n";

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Xho
 
A

Anno Siegel


[variable passing]
You could use typeglobs (or whatever it is I am supposed to call those
things). I don't particularly recommend it, but I have used it in a pinch.

use strict;

my $x='j' x 10;

print "$x\n";
foo3($x);
print "$x\n";

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Yes, but why?

sub foo4 { $_[ 0] =~ s/j/k/g }

or

sub foo5 { s/j/k/g for shift }

do the same thing directly.

Anno
 
X

xhoster


[variable passing]
You could use typeglobs (or whatever it is I am supposed to call those
things). I don't particularly recommend it, but I have used it in a
pinch.

use strict;

my $x='j' x 10;

print "$x\n";
foo3($x);
print "$x\n";

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Yes, but why?

The point of a short but complete example script (like people here are
always asking for) is to show how something is done. It is not to show why
it is done. The why is covered in the prose. I thought it was pretty
obvious why to do that.
sub foo4 { $_[ 0] =~ s/j/k/g }

unlike $_[0], the name of $y can be arbitrarily chosen. If you are going
to use the variable fifty times in your subroutine, that is quite a
benefit.

Xho
 
A

Anno Siegel


[variable passing]
You could use typeglobs (or whatever it is I am supposed to call those
things). I don't particularly recommend it, but I have used it in a
pinch.

use strict;

my $x='j' x 10;

print "$x\n";
foo3($x);
print "$x\n";

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Yes, but why?

The point of a short but complete example script (like people here are
always asking for) is to show how something is done. It is not to show why
it is done. The why is covered in the prose. I thought it was pretty
obvious why to do that.

You say "in a pinch", but I don't see the pinch that makes a solution
involving package variables attractive.
sub foo4 { $_[ 0] =~ s/j/k/g }

unlike $_[0], the name of $y can be arbitrarily chosen. If you are going
to use the variable fifty times in your subroutine, that is quite a
benefit.

You can have that lexically too:

for my $y ( shift ) {
$y =~ s/j/k/g;
}

(Admitted, it doesn't scale well for more than one variable.)

You know the reason to avoid package variables: exposure. If you have
a rogue function do_something() (say, imported from somewhere), which does

sub do_something {
# ....
our $y = 'something else';
}

then calling do_something() while "*y = \ $_[ 0]" is in effect has
unexpected and hard-to-trace consequences:

sub foo6 {
our $y;
*y = \ shift();
do_something()
$y =~ s/g/k/g;
}

now sets its argument to "somethink else".

Anno
 
M

Mark Seger

Anno said:
Mark Seger said:
Perl *does* pass scalars "by reference" in the sense that a parameter
value can be changed from within the function. Example:

sub foe {
$_[ 0] = 'new value';
}
my $x = 'old value';
fum( $x);
print "$x\n";

What I meant by 'pass by reference' is that any changes you make in the
function are reflected back at caller who will then see changes to that
parameter. I believe what perl does is 'pass by value', in that it
makes a copy of the data, thereby allowing you to change it without fear
of clobbering the orignal value.


You believe wrong. The code example above shows that changes to sub
arguments (the elements of @_) are indeed visible to the caller. The
"call-by-value" thing happens when you assign the content of @_ to
local variables, as in "my ( $x, $y, $z) = @_;". If you change $x etc.
in the sub body, nothing is changed in the environment. If you change
$_[ 0] etc. directly, it is.

yikes! I was indeed wrong. I guess my habit of ALWAYS saying
my $xyz=shift
for each parameter has protected me in my ignorance. :cool:

but from yours and other notes I think I'll be a lot more comfortable
with passing references around now.

-mark
 
X

xhoster

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Yes, but why?

The point of a short but complete example script (like people here are
always asking for) is to show how something is done. It is not to show
why it is done. The why is covered in the prose. I thought it was
pretty obvious why to do that.

You say "in a pinch", but I don't see the pinch that makes a solution
involving package variables attractive.

It wasn't attractive. Just better than the alternatives I could think of.
We desperately needed to refactor the code. But we even more desperately
needed an answer which couldn't wait until the refactoring was done.
sub foo4 { $_[ 0] =~ s/j/k/g }

unlike $_[0], the name of $y can be arbitrarily chosen. If you are
going to use the variable fifty times in your subroutine, that is quite
a benefit.

You can have that lexically too:

for my $y ( shift ) {
$y =~ s/j/k/g;
}

(Admitted, it doesn't scale well for more than one variable.)

And that was exactly the pinch that spurred me to use typeglobs[1].

Although I guess I could have used

foreach my $x (shift) {
foreach my $y (shift) {
foreach my $z (shift) {
foreach my $w (shift) {
foreach my $q (shift) {
.....
}}}}}

Which is not that much uglier than the typeglobs, and definitely cleaner
from a "symbolic reference like things are evil" POV. (BTW, before someone
asks, I didn't indent the above on purpose. Since I want all the foreachs
together to act essentially as one atomic aliasing operation, I want them
all indented together. I would have put them all on one line, but that
would be too long. Besides, if I am using foreach to do something out of
the ordinary, it deserves to look out of the ordinary.)


[1] Ok, maybe not exactly. Looking back at that actual code, there was one
more factor involved.

my @x=1..20;
print "@x\n";
foo6($foo,$bar,\@x,$baz,$and,\@many,$more);
print "@x\n";

sub foo6 {
our @fooarray;
*fooarray=$_[2];
@fooarray=reverse @fooarray; # legacy code.
};

Where the 'reverse' line is standing in for >1500 lines that use @fooarray
several hundred times, plus several similarly situated other variables.

That one I still don't know a quick non-typeglob solution to (other than
refusing to take over crappy code from other people. But then they would
probably refuse to pay me, which would make me sad.)

Xho
 
A

Anno Siegel

(e-mail address removed)-berlin.de (Anno Siegel) wrote:

sub foo3 {
our $y;
*y = \$_[0];
$y =~ s/j/k/g;
};

Yes, but why?
[...]
You can have that lexically too:

for my $y ( shift ) {
$y =~ s/j/k/g;
}

(Admitted, it doesn't scale well for more than one variable.)

And that was exactly the pinch that spurred me to use typeglobs[1].

Ah, I see.
Although I guess I could have used

foreach my $x (shift) {
foreach my $y (shift) {
foreach my $z (shift) {
foreach my $w (shift) {
foreach my $q (shift) {
....
}}}}}

Which is not that much uglier than the typeglobs, and definitely cleaner
from a "symbolic reference like things are evil" POV. (BTW, before someone
^^^^^^^^^^^^^^^^^^
Just package variables. I don't think they are evil, sometimes their
program-wide scope is just what is needed. Still the rule is that scopes
should be as small as possible.
asks, I didn't indent the above on purpose. Since I want all the foreachs
together to act essentially as one atomic aliasing operation, I want them
all indented together. I would have put them all on one line, but that
would be too long. Besides, if I am using foreach to do something out of
the ordinary, it deserves to look out of the ordinary.)

It's a perfectly reasonable way to write this bit of code, imo. The nesting
of blocks that indentation would draw attention to is an artifact, we have
no other way to express this in Perl. If there was syntax for walking
through multiple lists in parallel, we'd use that with a single level.
[1] Ok, maybe not exactly. Looking back at that actual code, there was one
more factor involved.

my @x=1..20;
print "@x\n";
foo6($foo,$bar,\@x,$baz,$and,\@many,$more);
print "@x\n";

sub foo6 {
our @fooarray;
*fooarray=$_[2];
@fooarray=reverse @fooarray; # legacy code.
};

Where the 'reverse' line is standing in for >1500 lines that use @fooarray
several hundred times, plus several similarly situated other variables.

....which used to be globals and had to be shoehorned into the parameter
list post factum, am I right?
That one I still don't know a quick non-typeglob solution to (other than

One could tie @fooarray appropriately (Brian's Tie::OneOff would make that
a snap), but that might scare your co-workers. Well, if they said it was
over-engineering they might be right.

With the typeglob solution, your variables are still globals, (just
controlled locally, which is also a gain), so you have come only part
of the way if elimination of globals was indeed the purpose.

After this discussion I'd use the nested for-loops where applicable,
particularly after you invented such a nice format for it. About
those arrays (and similarly used hashes) I don't know. I'd have
to code up a tie-solution to imagine how it looks in context, but
not now.

Anno
 
B

Brian McCauley

Anno said:
You know the reason to avoid package variables: exposure.

I think this is often over-played. Perl allows you to play about with
package variables in other packages just as it lets you fiddle with the
internal of objects. But it's fairly obvious when you are doing it.
If you have
a rogue function do_something() (say, imported from somewhere), which does

sub do_something {
# ....
our $y = 'something else';
}

If this was imported from elsewhere it would be talking about a $y in a
different package so there would be no problem. If it's not imported
and is defined within the same module then it's just the usual problem
of managing global variables. (Note when I say 'global' I don't mean
'package', I mean 'package or file-scoped lexical'). This is why you
should always minimize your use of global variables. But sometimes they
are the right tool for the job.
then calling do_something() while "*y = \ $_[ 0]" is in effect has
unexpected and hard-to-trace consequences:

sub foo6 {
our $y;
*y = \ shift();
do_something()
$y =~ s/g/k/g;
}

If you are going to use package variables it's wise to get into the
habit of using local().

That said I would _not_ use typeglobs for this reason - I'd use one of
the other approaches given in this thead.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,172
Messages
2,570,934
Members
47,479
Latest member
JaysonK723

Latest Threads

Top