Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Returning arrays from a package

by sherab (Scribe)
on Oct 01, 2019 at 23:29 UTC ( [id://11106931]=perlquestion: print w/replies, xml ) Need Help??

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

Hello monks, I am having a time of it trying to return arrays from a package. My package looks like this...
package xyz; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(@overall @electronics @safety); my @products = ( ["MG-20",1,2,3], ["JU-54",2,3,1], ["HY-21",3,1,2], ["OK-34",4,10,5], ["GT-75",9,6], ["KJ-23",6,8,7], ["PO-65",7,5,10], ["HN-34",8,6,9], ["ED-23",9,7,4], ["FR-98",10,4,8], ); my (@overall,@electronics,@safety) = breakouts(@products); sub breakouts { my (@LoL)=@_; my $i=0; my (@overall,@electronics,@safety); foreach(@LoL){ push @overall,@{$LoL[$i]}[1].":".@{$LoL[$i]}[0]; push @electronics,@{$LoL[$i]}[2].":".@{$LoL[$i]}[0]; push @safety,@{$LoL[$i]}[3].":".@{$LoL[$i]}[0]; $i++; } return(\@overall,\@electronics,\@safety); } 1;
My script looks like this....
#!/usr/bin/perl use strict; use warnings; use xyz qw(:DEFAULT @overall @electronics @safety); print "OVERALL\n"; print "@overall\n"; print "ELECTRONICS\n"; print "@electronics\n"; print "SAFETY\n"; print "@safety\n";
and nothing returns for @overall, @electronics, or @safety. I am sure that it comes down to my references returning from the subroutine, but perl isn't my primary language and I'm having a devil of a time figuring this out. I'm so close! Can anyone point me in the right direction?

Replies are listed 'Best First'.
Re: Returning arrays from a package
by jcb (Parson) on Oct 01, 2019 at 23:43 UTC

    Your problem is simple: you are declaring the arrays that you want to export as lexicals instead of globals in your code. Exporter then accesses the globals and ends up exporting empty arrays.

    Try our (@overall, @electronics, @safety); breakouts(@products); at line 23.

    You will also need to rework breakouts to directly load the arrays, instead of creating locals and attempting to return those. You cannot return multiple lists from a function in Perl — perl will merge them all into a single flat list and put it all in the first array.

      Please allow me to nitpick a bit and to advertise an experimental feature

      > You cannot return multiple lists from a function in Perl

      well the OP is trying to return multiple array-refs not lists, and the new'ish use feature qw/refaliasing/; allows these to be assigned to multiple @arrays. (see tst2())

      use strict; use warnings; use Data::Dump qw/pp dd/; use feature qw/refaliasing/; no warnings "experimental::refaliasing"; sub tst { (\my %args) = @_; pp \%args; } tst({a=>1,b=>2}); sub tst2 { return [1..3],[4..7] } our (@a,@b); (\@a,\@b) = tst2(); pp \@b; pp \@a;

      { a => 1, b => 2 } [4 .. 7] [1, 2, 3]

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        While we are nitpicking, I will note that the experimental refaliasing feature seems to just make references into lvalues, such that tst2 is actually returning a flat list of arrayrefs, rather than multiple lists. You still cannot return multiple lists in Perl. Oh, and did I mention it is an experimental feature? :-)

Re: Returning arrays from a package
by LanX (Saint) on Oct 01, 2019 at 23:45 UTC
    Hi

    this

    my (@overall,@electronics,@safety) =   ...

    can't work, because you are declaring private variables with my.

    Private vars stop to exist at end of their scope, which is at most the files boundary.

    But you are trying to export global package variables, with

    our @EXPORT_OK = qw(@overall @electronics @safety);

    so better try

    our (@overall,@electronics,@safety) =   ...

    our will make @overall et.al. act as aliases for @xyz::overall and the exporter can find them.

    (untested)

    HTH! :)

    update

    furthermore returning \@arrayrefs to @arrays doesn't work like this.

    Returning shouldn't be necessary if you declare the global variables prior to the function and assign directly inside.

    so better try something like

    our (@overall,@electronics,@safety); sub breakouts { my (@LoL)=@_; my $i=0; foreach(@LoL){ push @overall,@{$LoL[$i]}[1].":".@{$LoL[$i]}[0]; push @electronics,@{$LoL[$i]}[2].":".@{$LoL[$i]}[0]; push @safety,@{$LoL[$i]}[3].":".@{$LoL[$i]}[0]; $i++; } } breakouts(@products);

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Returning arrays from a package
by 1nickt (Canon) on Oct 02, 2019 at 00:52 UTC

    Hi, indeed you are close to something that works, but I recommend redesigning your solution to use Modern Perl, so that it not only works, but is extensible and maintainable.

    Exporting globals when you just need to return some data is not usually the best design. Your case seems to call for an object class with methods that return the needed data (although really it seems like you want to build a spreadsheet; if so, there are tools for that). It's also usually better to pass around references to arrays; sooner or later you'll want to pass something else along with but separate from the array.

    Here's one way using Moo to create a simple object class.

    package XYZ { use Moo; has [qw/_products overall electronics safety/] => ( is => 'lazy' ) +; sub _build__products { return [ ['MG-20',1,2,3], ['JU-54',2,3,1], ['HY-21',3,1,2], ['OK-34',4,10,5], ['GT-75',9,6,'n/a'], ['KJ-23',6,8,7], ['PO-65',7,5,10], ['HN-34',8,6,9], ['ED-23',9,7,4], ['FR-98',10,4,8], ]; } sub _build_overall { return [ map { join(':', @{ $_ }[1,0]) } @{ $_[0]->_products } + ]; } sub _build_electronics { return [ map { join(':', @{ $_ }[2,0]) } @{ $_[0]->_products } + ]; } sub _build_safety { return [ map { join(':', @{ $_ }[3,0]) } @{ $_[0]->_products } + ]; } }; use strict; use warnings; use feature 'say'; use XYZ; my $obj = XYZ->new; say 'OVERALL'; say for @{ $obj->overall }; say 'ELECTRONICS'; say for @{ $obj->electronics }; say 'SAFETY'; say for @{ $obj->safety }; __END__
    Output:
    perl 11106931.pl OVERALL 1:MG-20 2:JU-54 3:HY-21 4:OK-34 9:GT-75 6:KJ-23 7:PO-65 8:HN-34 9:ED-23 10:FR-98 ELECTRONICS 2:MG-20 3:JU-54 1:HY-21 10:OK-34 6:GT-75 8:KJ-23 5:PO-65 6:HN-34 7:ED-23 4:FR-98 SAFETY 3:MG-20 1:JU-54 2:HY-21 5:OK-34 n/a:GT-75 7:KJ-23 10:PO-65 9:HN-34 4:ED-23 8:FR-98
    (Note the need to provide a value for each column in your raw product data, to prevent warnings about uninitialized values.) <

    Hope this helps!


    The way forward always starts with a minimal test.
      Static data doesnt need classes

        Not always true. And the OP is working on a class, and it's likely s/he will want to do more with the numbers. So rather than suggest regressing in functionality, I went with his/her architecture. :-/


        The way forward always starts with a minimal test.
Re: Returning arrays from a package
by Anonymous Monk on Oct 04, 2019 at 02:00 UTC

    Hi,

    This is what you were trying to write

    Actually this is what you were trying to write

    #!/usr/bin/perl -- package xyz; require Exporter; *import = \&Exporter::import; use strict; use warnings; our @EXPORT_OK = qw(@overall @electronics @safety); our @products = ( [ "MG-20", 1, 2, 3 ], [ "JU-54", 2, 3, 1 ], [ "HY-21", 3, 1, 2 ], [ "OK-34", 4, 10, 5 ], [ "GT-75", 9, 6, 0 ], [ "KJ-23", 6, 8, 7 ], [ "PO-65", 7, 5, 10 ], [ "HN-34", 8, 6, 9 ], [ "ED-23", 9, 7, 4 ], [ "FR-98", 10, 4, 8 ], ); our @overall = map { $_->[1] . ':' . $_->[0] } @products; our @electronics = map { $_->[2] . ':' . $_->[0] } @products; our @safety = map { $_->[3] . ':' . $_->[0] } @products; 1; __END__

    So that you could write something like this (no exporting required)

    use Data::Dump qw/ dd/; use xyz qw/ @safety /;; dd( \@safety , \@xyz::safety ); __END__do { my $a = [ "3:MG-20", "1:JU-54", "2:HY-21", "5:OK-34", "0:GT-75", "7:KJ-23", "10:PO-65", "9:HN-34", "4:ED-23", "8:FR-98", ]; ($a, $a); }

    Also, all lowercase names are reserved for pragmas . You can find much much more discussions on naming modules in Re: RFC: Automatic logger module

    Sherab_Inc::xyz Sherab_LLC::products Xyz_LLC::products MySherabApp::xyz SherabApp::products ...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11106931]
Approved by jcb
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (4)
As of 2024-04-16 23:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found