CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
I'm having trouble with references...

 



DrZed
User

Jul 9, 2000, 7:42 AM

Post #1 of 4 (565 views)
I'm having trouble with references... Can't Post

Ultra short version, since the forum keeps refreshing and I've typed a verbose version twice now:

I want to create a data structure from a reference being passed.

For example:

sub x
{
my ($ref) = shift;

$ref->[0] = "One";
$ref->[1] = "Two";
$ref->[2] = "Three";
}
&x($r);
# $r->[0] eq "One"

I run into problems such as:

Not an ARRAY reference at dzlib-io.cgi line 278

It seems like the reference is already typed.

Would something like the following be stable:

sub x
{
my ($ref);
my (@a) = ("One","Two","Three");

$ref=\@a;
return $ref
}
$r=&x;
# $r->[0] eq "One"

The above looks like a bad idea, since $r will point to @a, which no longer exists.

However, taking a reference which is undefined and assigning values to it seems to work most but not all of the time.

If your interested, here are the functions I'm working with

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


################################################################
# DATA TO TEXT
#
# Usage:
# &data_to_text($DATA_REFERENCE)
# Description:
# This subroutine returns the data refered to by
# $DATA_REFERENCE in a formatted text structure. It will
# parse complex data sctructures, but can only parse the
# following types: SCALAR, ARRAY, HASH, REF. It will not
# parse, for example, CODE and GLOB.
# Parameters:
# $DATA_REFERENCE
# A pointer to any type of data structure that does
# not contain a glob. The data structure can be
# either simple or complex.
# Internal parameters
# The code also uses a number of internal parameters.
# These parameters are not designed to recieve data
# from outside calls. They are used during recursion.
# Result:
# The data is returned in a format designed to be accessable
# to text editors as well as to be loaded by the
# &text_to_data subroutine.
#
# References to external (to the data structure) variables:
# While data referenced will be saved and can therefore be
# loaded, if the REF referenced another data structure, the
# reference will NOT reference the other data structure upon
# being loaded.
#
# For example, if the data structure referenced an array
# named @colors. After saving and loading the data, the
# reference would reference an array with the same values as
# @colors. However, changes to @colors would be INDEPENDANT
# of changes to the reference.
#
# It would be possible to enchance this routine to allow for
# external references to be maintained. To do so, it would
# require any potentially referenced variables to have their
# names passed so that it could check for references to
# their addresses and record the variable's name. This has
# NOT be done.
#
# References to internal (to the data structure) variables:
# In the event that the REF referenced part of the data
# structure already parsed, it will note the internal
# reference and it will not duplicate the data. This will
# also prevent an infinite loop from forming in the instance
# where a data structure references itself.
#
sub data_to_text
{
my (%main_occ);
my ($r,$h,$occ_ref) = (shift,shift &#0124; &#0124; "",shift &#0124; &#0124; \%main_occ);
my ($type) = ref($r);
my ($addr) = scalar($r) &#0124; &#0124; "UNDEF";
my ($ret);

sub leading_header
{
my ($data,$header) = (shift,shift);
$data.="\n";
$data =~ s|^|$header|gm;
return $data;
}

$h=~s/ /\t/;

if (defined($occ_ref->{$addr}))
{
$ret = "$h|$addr\n";
$occ_ref->{$addr} = 2;
}
else
{
$ret = "$h<$type:$addr>\n";
$occ_ref->{$addr} = 1;
if ($type eq "SCALAR")
{ $ret .= &leading_header($$r,"$h |"); }
elsif ($type eq "ARRAY")
{ foreach (@$r) { $ret .= &data_to_text(\$_,"$h ",$occ_ref); } }
elsif ($type eq "HASH")
{ my ($key);
foreach $key (keys %$r)
{ $ret .= "$h <SCALAR>\n";
$ret .= &leading_header($key,"$h |");
$ret .= "$h </SCALAR>\n";
$ret .= &data_to_text(\$r->{$key},"$h ",$occ_ref);
}
}
elsif ($type eq "REF")
{ $ret .= &data_to_text($$r,"$h ",$occ_ref); }
elsif ($type eq "")
{ $ret .= "$h UNDEFINED\n"; }
else
{ $ret .= "$h TYPE NOT SUPPORTED: <$type>\n"; }
$ret .= "$h</$type>\n";
}
if ($h eq "")
{ # Strip out addresses that are not duplicated. e.g. <SCALAR:SCALAR(0xba6f74)> -> <SCALAR>
$ret =~ s!^(\s*<\w+) Frown.+)>!($occ_ref->{$2} == 2)?("$1:$2>") Frown"$1>")!gem;
# Reduce tags for single line values.
# e.g. the following....
# <SCALAR>
# |One liner.
# </SCALAR>
# .... would become the following....
# <SCALAR=One liner.>
# If there's a shared address, it would be as follows:
# <SCALAR:SCALAR(0xba6f74)=One liner.>
$ret =~ s!^(\s*<(\w+)[\:.+]?)>\n\s*\|(.*)\n\s*</\2>!$1=$3>!gm;
}

return $ret;
}
################################################################

################################################################
# TEXT TO DATA
#
# Usage:
# &text_to_data($DATA_REFERENCE, $TEXT_REFERENCE)
# Description:
# This subroutine parses the text referenced by
# $TEXT_REFERENCE and creates the data structure encoded in
# the text using the variable referenced by $DATA_REFERENCE.
# It can parse complex data structures, but can only parse
# the following types: SCALAR, ARRAY, HASH, REF. It will
# not parse, for example, CODE and GLOB.
# Parameters:
# $DATA_REFERENCE
# A reference to the variable to be assigned a value.
# The variable referenced will be type checked, but
# the type checking will rely on the existance of the
# &diag routine.
# $TEXT_REFERENCE
# A reference to the text to be parsed. This text is
# passed using a reference to avoid making multiple
# copies for each recursion of what will probably be
# large amounts of text.
# Internal parameters
# The code also uses a number of internal parameters.
# These parameters are not designed to recieve data
# from outside calls. They are used during recursion.
# Result:
# The result is a data structure based on the recieved text.
#
# References to external (to the data structure) variables:
# While data referenced will be loaded, if the REF
# originally referenced another data structure outside of
# the data structure referenced, the reference will NOT
# reference the other data structure upon being loaded.
#
# For example, if the data structure referenced an array
# named @colors. After saving and loading the data, the
# reference would reference an array with the same values as
# @colors. However, changes to @colors would be INDEPENDANT
# of changes to the reference.
#
# It would be possible to enchance this routine to allow for
# external references to be maintained. To do so, it would
# require any potentially referenced variables to have their
# names passed so that it could check for references to
# their addresses and record the variable's name. This has
# NOT be done.
#
# References to internal (to the data structure) variables:
# In the event that the REF referenced part of the data
# structure already parsed, it will note the internal
# reference and it will not duplicate the data.
#
sub text_to_data
{
my (%main_occ,@main_lines);
my ($data_ref,$text_ref) = (shift,shift);
my ($occ_ref,$lines_ref) =
(shift &#0124; &#0124; \%main_occ,
shift &#0124; &#0124; &split_into_lines($text_ref,\@main_lines));
my ($line_count) = (shift &#0124; &#0124; $#{$lines_ref});

sub split_into_lines
{
my ($sil_text_ref,$sil_lines_ref) = @_;
@{$sil_lines_ref} = split (/\n/,${$sil_text_ref});
return $sil_lines_ref
}

my ($line) = shift(@{$lines_ref});

if ($line =~ m/^\s*<(\w+)( Frown\w+\(\w+\)))?(=(.+))?>$/)
{
my ($type) = $1;
my ($addr) = $3;
my ($value) = $5;

#&dzlib_io_error("LINE=$line; TYPE=$type; ADDR=$addr; VALUE=$value; ref()=",ref($data_ref));

# Check type vs. reference

if ( ( $type ne ref($data_ref) ) &&
( ( $type ne "REF" ) &#0124; &#0124; ( ref($data_ref) ne "SCALAR" ) ) )
{ dzlib_io_error(qq{Mismatched data types.
Expected: },ref($data_ref),qq{
Recieved: $type
Line number: },($line_count-$#{$lines_ref}));
}

# If the address of the data structure is defined,
# note the reference to the data in the hash referenced by $occ_ref.

if (defined($addr)) { $occ_ref->{$addr} = $data_ref; }

# If the value was in the tag (i.e. it's a one-liner),
# then $value is already set. Otherwise, set it by
# reading multiple lines.

my ($entry);

if (!defined($value))
{
if ($type eq "ARRAY")
{ while ($lines_ref->[0] !~ m|^\s*</|)
{ &text_to_data(\$entry,$text_ref,$occ_ref,$lines_ref,$line_count);
push(@{$data_ref},$entry);
}
}
elsif ($type eq "HASH")
{ while ($lines_ref->[0] !~ m|^\s*</|)
{ &text_to_data(\$value,$text_ref,$occ_ref,$lines_ref,$line_count);
&text_to_data(\$entry,$text_ref,$occ_ref,$lines_ref,$line_count);
$data_ref->{$value}=$entry;
}
}
elsif ($type eq "REF")
{ my ($ref);
&text_to_data(\$ref,$text_ref,$occ_ref,$lines_ref,$line_count);
${$data_ref} = \$ref;
}
elsif ($type eq "SCALAR")
{
$value="";
$line = shift(@{$lines_ref});
while ( $line =~ m/^\s*\|/ )
{ $value.="$'\n";
$line = shift(@{$lines_ref});
}
chomp($value);
if ($line !~ m|^\s*</$type>$|)
{ $line =~ m/^\s*(.*)/;
dzlib_io_error(qq{Improper close of data text.
Expected: </$type>
Recieved: $1
Line number: },($line_count-$#{$lines_ref}));
}
${$data_ref}=$value;
}
else
{ dzlib_io_error("Invalid type: $type");
}
}
else
{
if ($type eq "ARRAY") { dzlib_io_error("Invalid direct assignment of an array.\n$line"); }
elsif ($type eq "HASH") { dzlib_io_error("Invalid direct assignment of a hash.\n$line"); }
elsif ($type eq "REF") { ${$data_ref}=$occ_ref->{$value}; }
elsif ($type eq "SCALAR") { ${$data_ref}=$value; }
else
{ dzlib_io_error("Invalid type: $type\nValue: $value");
}
}
}
else
{ dzlib_io_error(qq{Improper start of data text.
Recieved: $line
Line number: },($line_count-$#{$lines_ref}));
}
}
################################################################

</pre><HR></BLOCKQUOTE>


japhy
Enthusiast

Jul 9, 2000, 8:52 AM

Post #2 of 4 (565 views)
Re: I'm having trouble with references... [In reply to] Can't Post

Hey Zed. First, let me clear up something you said that was wrong:

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


{
my $foo = "bar";
$ref_to_foo = \$foo;
}
print $$ref_to_foo; # "bar"
</pre><HR></BLOCKQUOTE>

As you can see, even though $foo goes out of scope, its reference count (refcount) is 1 (since $ref_to_foo refers to it), so it retained in memory. This is how practically every object-oriented module in Perl works:

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


sub SomeClass::new {
my ($class) = @_;
my $self = { initial => 'data' };
bless $self, $class;
}
$obj = SomeClass->new;
</pre><HR></BLOCKQUOTE>

bless() returns the reference once it has been blessed into the class. $self was a my() variable, but it lives on in memory as long as $obj is around.

Anyway, as for "not an ARRAY reference"... I'm guessing your function is NOT getting an array reference, NOR an undef value, NOR a string. You can turn an array reference, an undef value, or a string into an array reference using @$var = (...) syntax:

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


$a = [1,2,3];
@$a = (4,5,6); # 1
undef $a;
@$a = (7,8,9); # 2
$a = "foo";
@$a = (10,11,12); # 3
</pre><HR></BLOCKQUOTE>

Of course, #3 is icky, since it's a symbolic reference, and creates a @foo array indirectly. But the fact remains that doing

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


$a = "foo";
@$a = (10,11,12);
# allows you to do
print $a->[1];
</pre><HR></BLOCKQUOTE>

So I'm guessing your function is getting a reference to some other data structure.

Oh, and if you're going to show a lengthy block of code (such as a program, or this weighty behemoth you've pasted here), it would be best to keep it as a text file online somewhere. It saves some cut-and-paste time, as well as the annoyance of text-to-smiley conversion in code.

NOTE TO JASMINE: can you add a filter to UBB that stops text-to-smiley conversion inside code and /code tags? Or at least a "turn off smilies for this post" option? It would be much appreciated.


Kanji
User

Jul 9, 2000, 9:42 AM

Post #3 of 4 (565 views)
Re: I'm having trouble with references... [In reply to] Can't Post

You could always do something like ...

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>

sub x {
my $ra = @_ ? shift # use array ref in $_[0]
: [ ]; # or create our own

@$ra = qw( One Two Three );


return $ra;
}</pre><HR></BLOCKQUOTE>

... which'd allow you to do either ...

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>

# Example 1
my $ra_x = [ ]; # create array ref
x( $ra_x ); # $ra_x->[0] == One

# Example 2
my $ra_x = x(); # $ra_x->[0] == One</pre><HR></BLOCKQUOTE>

... but you'd still need to ensure you're passing x() an array ref (as in Example 1) though.

Anything else (which is where I suspect you went awry) would cause the same 'Not an ARRAY ref ...' error you've been seeing.

Perhaps a ref($ra) eq 'ARRAY' test in x() itself would help save your sanity, as I don't recall if you can enforce that with prototypes.


DrZed
User

Jul 10, 2000, 8:47 AM

Post #4 of 4 (565 views)
Re: I'm having trouble with references... [In reply to] Can't Post

Thanks all.

I got it working. I'm going to try to clean up the code a bit, but I have a working version.

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives