sankoshy has asked for the wisdom of the Perl Monks concerning the following question:

*************** sub BuildLeaf { my ($hostname, $lo_add, $ospf_process, $ospf_area, $bgp_as, $bgp_p +ass, @VRF_names, @spines_ip) = @_; #$ do a whole bunch of stuff here ## } BuildLeaf ($leaf_info[0][[$i]],$leaf_info[[1]][[$i]],"Underlay",$ospf_ +area,$bgp_as,$bgp_MD5_pass,@{@vrf_info[0]},@{@spine_info[[1]]}); ***************

When I pass the arrays to the sub Buildleaf using arrays vrf_info and spine_info, for some reason array vrf_info gets populated with the vales from array spine_info and spine_info becomes empty. What gives ?

Replies are listed 'Best First'.
Re: Pass Arrays to a subroutine
by davido (Cardinal) on Nov 27, 2015 at 04:15 UTC

    As mentioned in perlsub, parameters passed into a subroutine are passed as a flat list. From the subroutine's perspective, it's impossible to determine where @VRF_names ends, and @spines_ip begins. It's all just a flat list of values.

    You want to pass in a reference to an array:

    sub BuildLeaf { my ( $hostname, $lo_add, $ospf_process, $ospf_area, $bgp_as, $bgp_pass, $VRF_names_aref, $spines_ip_aref ) = @_; # Do your stuff here. }

    And then in the sub call, change @{$vrf_info[0]} and @{$spine_info[1]} to $vrf_info[0] and $spine_info[1]. Within the subroutine, you will need to dereference the array-refs appropriately. See perlref and perlreftut for details. We can't really guess how you might be using the passed-in arrays without seeing the code, so it would be impractical for us to demonstrate the appropriate array-ref dereferencing for your particular use case. But the documentation is good, although possibly a bit opaque on first read. The online book Modern Perl would probably offer a gentler introduction to wielding references.


    Dave

Re: Pass Arrays to a subroutine
by Laurent_R (Canon) on Nov 27, 2015 at 07:20 UTC
    As already menionned by davido, if you pass two arrays to a sub, they get flattened into one bigger list. So you need to pass array references to the sub.

    As an example, this is one possible way to pass array references to a sub and to use them within the sub.

    my @a = (1, 2, 3); my @b = (5 .. 7); process(\@a, \@b); # pass array references to the sub sub process { my ($a1ref, $a2ref); # retrieve the params my @array1 = @{$a1ref); # dereference $a1ref inti an array my @array2 = @{$a2ref); # ... process @array1 and @array2 # ... }
    Update: removed underscores from $a1_ref and $a2_ref variable in initialization to make variable names consistent with the following code lines.
Re: Pass Arrays to a subroutine
by neilwatson (Priest) on Nov 27, 2015 at 14:48 UTC

    I like named params

    mysub({ a1 => \@array1, a2 => \@array2 }); sub mysub{ my ( $argref ) = @_; # brackets matter # ref to @array1 is @{ $argref->{a1} } # ref to @array2 is @{ $argref->{a2} }
    More examples here

    Neil Watson
    watson-wilson.ca

      I always used to accept my params within a hashref, but in the last year or so, I've changed to just using a straight up hash instead. I feel it makes the interface a tiny bit cleaner:

      mysub(a => \@array1, b => \@array2); sub mysub { my %params = @_; print "$_\n" for @{ $params{a} }; }
Re: Pass Arrays to a subroutine
by AnomalousMonk (Archbishop) on Nov 27, 2015 at 17:36 UTC
    BuildLeaf ($leaf_info[0][[$i]],$leaf_info[[1]][[$i]], ...);

    This may be just a cut-and-paste problem in posting the OP, but the array indexing used in the example code is doubleplusungood.

    An expression like  [1] or  [$i] on its own builds an anonymous array and evaluates as the reference address of the array; this reference address is the only way to access the array, hence "anonymous". A reference address evaluated in numeric context is some (probably rather large) number. The expression  $leaf_info[0][[$i]] evaluates the anonymous array reference returned by  [$i] in the numeric context of an array element index and immediately expands the array  @leaf_info (or to be precise, the anonymous array that is the zeroth element of @leaf_info) to include the indexed array element. (Update: Well, not quite. See Update 2 below.) I doubt this is what you want. E.g.:

    c:\@Work\Perl\monks>perl -wMstrict -le "my @ra; ;; $ra[3] = 42; print 'A: elements in array: ', scalar @ra; ;; $ra[[1]] = 99; print 'B: elements in array: ', scalar @ra; " A: elements in array: 4 Use of reference "ARRAY(0x5cc15c)" as array index at -e line 1. B: elements in array: 6078813
    The expression  $leaf_info[[1]][[$i]] is horrifically worse | just as bad. (Update: Indeed, it will cause Perl to attempt to build a multi-terabyte array | actually, no; see Update 2 below, another reason for doubting this is the original code.) Note that had you had warnings enabled, Perl would have chided you about these missteps (if, in fact, they exist in the original code).

    Update:

    BuildLeaf (...,@{@vrf_info[0]},@{@spine_info[[1]]});
    This is something else that warnings would have warned you about:  @vrf_info[0] is a single-element array slice and is frowned upon, although it works just fine. We will pass quickly by  @spine_info[[1]] with eyes averted and muttering a brief, earnest prayer.

    Update 2: My code example above shows changes to the array produced by write accesses; the OPed code showed pure read accesses, and these produce different effects:

    c:\@Work\Perl\monks>perl -wMstrict -le "my @ra; my $x; ;; S($ra[3]); print 'A: elements in array: ', scalar @ra; ;; S($ra[[1]][[0]]); print 'B: elements in top level array: ', scalar @ra; print 'C: elements in 2nd level array: ', scalar @{ $ra[-1] }; ;; sub S { $x = defined($_[0]) . ''; } " A: elements in array: 0 Use of reference "ARRAY(0x94c05c)" as array index at -e line 1. Use of reference "ARRAY(0x432004)" as array index at -e line 1. B: elements in top level array: 9748573 C: elements in 2nd level array: 0
    The internal details differ, but the bottom line is the same:  $leaf_info[0][[$i]] et al are not ways in which you ever want to index an array.


    Give a man a fish:  <%-{-{-{-<