1

I am building a program with several routines I'd like to be able to use over and over. Oh, I'm also an absolute beginner with perl, so there's that. So, I have arrays that I fill with lines of text I pull out of files so I can parse, modify, compare, etc., with either user input or other bits of data I pull from (an)other file(s) depending on the program the subroutine is deployed in.

So, I have one subroutine I pass three array references to:

@sorted = &sort_arrays(\@order, \@ktms, \@sorted);

I dereference the arrays after passing in the sub for a sanity check like so:

sub sort_arrays {
my ($ref_array, $list_array, $sorted_r) = @_;
print "@{$ref_array} \n"; print "@{$list_array} \n"; print "@{$sorted_r} \n";

and I get the values of each cell of each array printed on a single line each with a single space between them. Great! I actually had this working as an individual program to sort a random generated file from a master based on the order the random values appear in the master. Now, I am trying to make other subroutines generic and reusable with references, but I'm not having the same luck with dereferencing them. For example:

@that = &get_ktms_from_program(\@this, \@that);

But, when I try to dereference them, I get bad news!

    print "\nEntered get_lines_from_program sub\n"; 
    my ($lines_r, $parsed_r) = @_;
    print "@{$lines_r}\n";

The output:

Entered get_lines_from_program sub
ARRAY(0x81c20dc)

So, for some reason, I am unable to dereference THIS array with the same method used earlier. What gives? TIA for the help!

WetCheerios
  • 155
  • 7
  • 1
    There's no need to use `&` to call subroutines; in fact, it can have other effects you probably didn't intend as detailed in [perlsub](https://perldoc.pl/perlsub). – Grinnz Oct 10 '18 at 22:27
  • Woah! Makes current @_ visible to called subroutine?!? That's awesome! But, I am calling each subroutine from the main. I do utilize @ARGV to pull in a filename; I don't think that's happening here though. Perhaps, but not sure. I'll check my data before referencing it to make sure I do not have array references in my arrays before referencing them. These multidimensional arrays and hashes are intense! – WetCheerios Oct 11 '18 at 01:35
  • If you got `Entered get_lines_from_program sub ARRAY(0x81c20dc)`, it's because `@this` contains a single element that's a reference to an array. In other words, it looks like you have a bug in the code you didn't show us – ikegami Oct 11 '18 at 04:26

2 Answers2

2

This depends on what you do with those references in your subs. Here are some notes on that

If you pass a reference and work with the reference

func(\@ary);
...
sub func {
    my ($ra) = @_;
    ...
    push @$ra, @some_values;  # changes @ary in the caller
}

then you've just changed the @ary in the caller.

However, if you create a local copy in the sub

sub func {
    my ($ra) = @_;
    my @local_ary = @$ra;
    ...
    return \@local_ary;    # return a reference to a brand new @local_ary
}

then changes to @local_ary don't affect the @ary in the caller (unless it's involved with the return itself of course -- being overwritten by it, or the return being pushed onto it).

Another point: arguments passed to a subroutine are aliased in @_ so if in the sub you work with $_[0] (etc) then you may be directly changing data in the caller.

From what is shown it is clear that @lines_r has one element, which itself is an array reference. How it got there is hard to tell without seeing the code for that. One possibility is that you returned an arrayref from some function, return \@local_ary, which you didn't dereference in the caller but merely added it to @this or @that (referenced by $list_r).


A few comments on the code in the question

The & in front of a sub has subtle effects, which you almost certainly don't need. It used to be needed a long, long time ago but now it isn't and shouldn't be used for a "normal" call.

Arrays are passed to the sub by reference, which is good as it avoids possible massive amounts of data copy. However, you clearly return a list, as it is assigned directly to arrays. If those arrays may have a lot of data better return the reference, and dereference it in the caller

my $ra = func(...);   # func() returns an array reference
my @ary = @$ra;

or

my @ary = @{ func(...) };

I'd consider always returning a reference for an array with data (as opposed to a small collection of scalar variables to return in a list, where the choice depends merely on caller's preferences).

But above all this, also consider that there is normally no reason to dereference the returned arrayref in order to create another array; it's yet more data copy, which is expensive, while you can do any and all that you need with the array reference anyway (see end of this post for instance).

Finally, be very, very careful with what you are doing if you pass an array by reference and assign the return to that same array. I'd really just pass on that idea.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • I had that backwards. As you probably noticed, I tried to camouflage the nature of my code, and failed. The sorted routine returns the value of the referenced array @sorted: `@sorted = sub(\@reference_ar, \@arraytosort_ar, \@sorted);` When I finished writing that it occurred to me that it is dumb to pass a reference for an array I am setting equal to the returned value of the sub... Sheesh! Thanks! – WetCheerios Oct 11 '18 at 01:39
1

It is likely that you stored this array reference in an array somewhere. This means you now have an array with one element: an array reference. (References are how you nest data structures in Perl; see perllol). When you interpolate this array into a string, the one element (array reference) is printed, and the stringified form of an array reference looks like the string you saw. Instead, store the array reference in a scalar variable wherever you retrieve it, which can be passed to your other subroutines as-is.

use strict;
use warnings;

my $aref = sub_returning_aref();
other_sub($aref);

sub sub_returning_aref {
  my @stuff;
  return \@stuff;
}

sub other_sub {
  my ($aref) = @_;
  print "@$aref\n";
}

The key to remember is that \@array returns a reference which is a scalar value, and can then be used as any other scalar, but must be dereferenced to yield the array contents.

Data::Dumper is a good core tool for determining what exactly you have in a variable when debugging.

use Data::Dumper;
print Dumper \@array;
Grinnz
  • 9,093
  • 11
  • 18
  • Thanks for the answer! I say I'm brand new to Perl, but I'm just masking my ineptitude at learning programming languages... any of them.. I have some formal education in perl, two classes provided through my employer, but I'm having to sneak in my actual utilization of the tool with my normal job function. So, I go days, or over a week sometimes without having any exposure to it. I am aware of the multidimensional capabilities of arrays, and will eventually need to use the language to interface with SQL tables to maintain current information on thousands and thousands of our products. YAY! – WetCheerios Oct 11 '18 at 01:44