access anonymous variable

G

George Mpouras

At the following example I want to alter the value of $counter variable ,
but without using the the $iter code reference.
I 've tried many tricks using __ANON__ but without luck . Can you help ?


my $iter = Create_counter( 37);

for (1..3)
{
print "$_) ". $iter->() ."\n"
}

sub Create_counter
{
my $counter = $_[0];

return sub
{
$counter++
}
}
 
T

Ted Zlatanov

GM> At the following example I want to alter the value of $counter variable ,
GM> but without using the the $iter code reference.
GM> I 've tried many tricks using __ANON__ but without luck . Can you help ?

GM> my $iter = Create_counter( 37);

GM> for (1..3)
GM> {
GM> print "$_) ". $iter->() ."\n"
GM> }

GM> sub Create_counter
GM> {
GM> my $counter = $_[0];

GM> return sub
GM> {
GM> $counter++
GM> }
GM> }

Here's one way:

#!/usr/bin/perl

use warnings;
use strict;
use Modern::perl;

{
my $counter = 0;

sub get_counter
{
$counter = shift @_ if scalar @_;
return \$counter;
}
}

${get_counter(37)}++; # increments the hidden $counter, starting at 37
say ${get_counter()};
${get_counter()}++; # increments the hidden $counter
say ${get_counter()};

This passes around a reference to a scoped variable; there's no other
way to access $counter outside its scope and the value of $counter will
persist between get_counter() calls.

I'll warn you that this is probably the wrong way to go unless you
really need this and know why. Can you explain your requirements a
little bit?

Ted
 
P

Peter Makholm

George Mpouras said:
At the following example I want to alter the value of $counter variable ,
but without using the the $iter code reference.

If you really need this, it can be done with the PadWalker module. But
I would recommend against it unless you know what you are doing and
are sure that you need to do it.

//Makholm
 
G

George Mpouras

Στις 1/2/2011 5:25 μμ, ο/η Ted Zlatanov έγÏαψε:
GM> At the following example I want to alter the value of $counter variable ,
GM> but without using the the $iter code reference.
GM> I 've tried many tricks using __ANON__ but without luck . Can you help ?

GM> my $iter = Create_counter( 37);

GM> for (1..3)
GM> {
GM> print "$_) ". $iter->() ."\n"
GM> }

GM> sub Create_counter
GM> {
GM> my $counter = $_[0];

GM> return sub
GM> {
GM> $counter++
GM> }
GM> }

Here's one way:

#!/usr/bin/perl

use warnings;
use strict;
use Modern::perl;

{
my $counter = 0;

sub get_counter
{
$counter = shift @_ if scalar @_;
return \$counter;
}
}

${get_counter(37)}++; # increments the hidden $counter, starting at 37
say ${get_counter()};
${get_counter()}++; # increments the hidden $counter
say ${get_counter()};

This passes around a reference to a scoped variable; there's no other
way to access $counter outside its scope and the value of $counter will
persist between get_counter() calls.

I'll warn you that this is probably the wrong way to go unless you
really need this and know why. Can you explain your requirements a
little bit?

Ted


I was doing some experiments after a discussion with my colleague about
iterators and how they can protect their variables. I thought I could
find a way for direct access private variable of an anonymous subroutine
but maybe it is impossible after all. They are completely invisible.

use Data::Dumper;
$Data::Dumper::Deparse=1;
print STDOUT Data::Dumper::Dumper(\%{__PACKAGE__.'::'});
 
T

Ted Zlatanov

GM> I was doing some experiments after a discussion with my colleague
GM> about iterators and how they can protect their variables. I thought I
GM> could find a way for direct access private variable of an anonymous
GM> subroutine but maybe it is impossible after all. They are completely
GM> invisible.

GM> use Data::Dumper;
GM> $Data::Dumper::Deparse=1;
GM> print STDOUT Data::Dumper::Dumper(\%{__PACKAGE__.'::'});

The example I gave shows how to have a scope that keeps a private
variable whose value is preserved between subroutine calls, which is
usually what an iterator needs. If you explain your actual need, it
would help us answer your question better.

Ted
 
J

jl_post

At the following example I want to alter the value of $counter variable ,
but without using the the $iter code reference.
I 've tried many tricks using __ANON__ but without luck . Can you help ?

my $iter = Create_counter(  37);

for (1..3)
{
print "$_) ". $iter->() ."\n"
}

sub Create_counter
{
my $counter = $_[0];

return sub
  {
  $counter++
  }
}


Dear George,

The problem with trying to alter the $counter variable is that it
is a variable that only gets modified inside the Create_counter()
subroutine and the function it returns.

Basically, you're using a closure. Read up on "perldoc -q closure"
for a better idea of what a closure is.

When you assign the subroutine reference to $iter with the line:

my $iter = Create_counter(37);

you're giving $iter the only power to modify the $counter variable
that was created in the call to Create_counter() . From here on, the
only way to modify that specific instance of $counter is to call $iter-

You can change all that if you modify the code, however. If you
change the Create_counter() subroutine to look like:

sub Create_counter
{
my $counter = $_[0];

return \$counter, sub { $counter++ }
}

then you can call it like this:

my ($counterRef, $iter) = Create_counter(37);

and you can see and modify the value of the $counter by printing or
assigning to $$counterRef (note the double '$' -- that's not a typo).
What's more is that you can still call Create_counter() the old way,
like this:

my $iter = Create_counter(37);

which will just discard the $counterRef part (due to assigning to a
scalar). However, if you do this, then you lose the ability to
observe and modify the $counter instance that $iter->() uses (other
than just calling $iter->(), of course).

If you prefer to avoid references, you can write Create_counter()
this way:

sub Create_counter
{
my $counter = $_[0];

return sub { $counter }, # getter
sub { $counter = $_[0] }, # setter
sub { $counter++ } # iterator
}

This will allow you to write code like this:

my ($getter, $setter, $iter) = Create_counter(37);

print $iter->(); # prints 37 and sets $counter to 38
print $getter->(); # prints out $counter, which is 38
$setter->(5); # sets $counter to 5
print $iter->(); # prints 5 and sets $counter to 6

So if you want to be able to read/modify the $counter variable that
$iter->() uses (without using $iter), you have to somehow return
access to it, by either returning a reference to it, or by returning
another sub { } that can read/modify it.

I hope this helps, George.

Have a great day!

-- Jean-Luc
 
S

sln

At the following example I want to alter the value of $counter variable ,
but without using the the $iter code reference.
I 've tried many tricks using __ANON__ but without luck . Can you help ?

my $iter = Create_counter(  37);

for (1..3)
{
print "$_) ". $iter->() ."\n"
}

sub Create_counter
{
my $counter = $_[0];

return sub
  {
  $counter++
  }
}


Dear George,

The problem with trying to alter the $counter variable is that it
is a variable that only gets modified inside the Create_counter()
subroutine and the function it returns.

Basically, you're using a closure. Read up on "perldoc -q closure"
for a better idea of what a closure is.

When you assign the subroutine reference to $iter with the line:

my $iter = Create_counter(37);

you're giving $iter the only power to modify the $counter variable
that was created in the call to Create_counter() . From here on, the
only way to modify that specific instance of $counter is to call $iter-

What about:

return sub {
if ($_[0]) {
$counter = $_[0];
} else {
  $counter++
}

then $iter->(0) and $iter->(),
or is that too much out of range of the intended use of an itterator?

-sln
 
J

jl_post

What about:

 return sub {
          if ($_[0]) {
             $counter = $_[0];
          } else {
             $counter++
       }

then $iter->(0) and  $iter->(),
or is that too much out of range of the intended use of an itterator?


That would work for setting $counter -- but only to a non-zero
value. (The way it is now, $iter->(0) will increment $counter.)

As for peeking at the value of $counter (that is, reading it
without modifying it), you might have to do this:

print $iter->( $iter->() );

but that might still increment the $counter, depending on when exactly
the '++' operator decides to fire off. (I'm not sure if that's well
defined in this case or not.)

Cheers,

-- Jean-Luc
 
S

sln

What about:

 return sub {
          if ($_[0]) {
             $counter = $_[0];
          } else {
             $counter++
       }

then $iter->(0) and  $iter->(),
or is that too much out of range of the intended use of an itterator?


That would work for setting $counter -- but only to a non-zero
value. (The way it is now, $iter->(0) will increment $counter.)

As for peeking at the value of $counter (that is, reading it
without modifying it), you might have to do this:

print $iter->( $iter->() );

but that might still increment the $counter, depending on when exactly
the '++' operator decides to fire off. (I'm not sure if that's well
defined in this case or not.)

'if ($_[0])' I didn't see that foible.

I guess poke and peak could be done
 return sub {
          if (defined $_[0]) {
             $counter = $_[0];
          } else {
             $counter++
       }
print $iter->( $iter->() - 1 ); # but this isin't understandable

or ---
 return sub {
          if (defined $_[0]) {
             $counter = ($_[0] =~ /[a-zA-Z]/) ? $counter : $_[0];
          } else {
             $counter++
       }
print $iter->( 'getval' );
print $iter->(0);

As long as the itterator does its main function correctly,
anything could be done in the sub, right?

-sln
 

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

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top