1

I have 2 empty arrays that I add items to using a for loop.

@main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
);
@arr1 = (); #only gets the letters
#supposed to look like:
#( 
#[a,b,c]
#[d]
#[e,f,g,h]
#[i,j]
#)

@arr2 = (); #only gets the numbers
#supposed to look like:
#( 
#[1,2,3]
#[4]
#[5,6,7,8]
#[9,10]
#)
for($i=0;@main;$i+=1){
    @line = split(/\s+/,shift(@main));
    push(@arr1,[]);
    push(@arr2,[]);
    while(@line){
        push(@arr1[$i],shift(@line));
        push(@arr2[$i],shift(@line));
    }
}

error:

Experimental push on scalar is now forbidden at index.pl line 29, near "))"
Experimental push on scalar is now forbidden at index.pl line 30, near "))"

It seems that @arr[$i] returns a reference to an array. How do I get this array and add items to it?

0

3 Answers 3

2

The first argument of push must be an array, not an array slice. I think you wanted @{ $arr1[$i] } (but that's not your only problem). Make sure that you're using use strict; use warnings;! –ikegami

for(my $i=0;@main;$i+=1){
    my @line = split(/\s+/,shift(@main));
    push(@arr1,[]);
    push(@arr2,[]);
    while(@line){
#             ↓↓           ↓ 
        push( @{ $arr1[$i] } ,shift(@line));
        push( @{ $arr2[$i] } ,shift(@line));
    }
}

Not exactly sure how it works, probably turns the reference into an actual array of something along those lines.

Another solution I came up with myself, which adds the finished array after instead of adding values slowly:

for($i=0;@main;$i+=1){
    @line = split(/\s+/,shift(@main));
    @subArr1 = ();
    @subArr2 = ();
    while(@line){
        push(@subArr1,shift(@line));
        push(@subArr2,shift(@line));
    }
    push(@arr1,[@subArr1]);
    push(@arr2,[@subArr2]);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Re "probably turns the reference into an actual array of something along those lines.", Exactly that. The technical term is "dereferences".
Easier to just make a lexically scoped array and add my @tmp1; ... push @arr1, \@tmp1;
Since you use index only to get elements then iterate over elements, for my $el (@main) {...}. This is much nicer to work with than the "C-style" for loop. (And if you did need an index itself you can still do for my $i (0..$#ary) {...], where syntax $#ary is for the last index of @ary.) If you need to empty the array in the process for some reason (a queue?), then can do while (my $el = shift @main) {...} (and $el exists inside the body of while (and continue if it's there)
2

As of perl 5.36, you can iterate over multiple elements of a list at once in a foreach loop, which lets you clean this up a bit:

#!/usr/bin/env perl
use 5.036;
# The next two lines aren't strictly needed because of the above, but
# I like to be explicit
use strict;
use warnings;
use experimental qw/for_list/;
use Data::Dumper;

my @main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
    );
my (@arr1, @arr2);

for my $line (@main) {
    my (@sub1, @sub2);
    for my ($name, $num) (split " ", $line) {
        push @sub1, $name;
        push @sub2, $num;
    }
    push @arr1, \@sub1;
    push @arr2, \@sub2;
}

print Dumper(\@arr1, \@arr2);

You can use the same approach in older versions using natatime from the List::MoreUtils module (Available from CPAN or your OS's package manager):

#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw/natatime/;
use Data::Dumper;

my @main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
    );
my (@arr1, @arr2);

for my $line (@main) {
    my (@sub1, @sub2);
    my $it = natatime 2, split(" ", $line);
    while (my ($name, $num) = $it->()) {
        push @sub1, $name;
        push @sub2, $num;
    }
    push @arr1, \@sub1;
    push @arr2, \@sub2;
}

print Dumper(\@arr1, \@arr2);

The thing these have in common is building up each subarray first, and then pushing references to them onto @arr1 and @arr2 at the end, removing the need for a lot of the arrayref dereferencing syntax. They also iterate directly over the elements you're storing in the subarrays instead of modifying the source directly with shift.

Comments

1

Please inspect the following demonstration code for a compliance with your problem.

The code is based upon comments in OP's code with description what result arrays should look alike.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my(@main,@arr1,@arr2);

@main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
);

for ( @main ) {
    push(@arr1,[ /(\d+)/g   ]);
    push(@arr2,[ /([a-z])/g ]);
}

say Dumper(\@arr1,\@arr2);

Output

$VAR1 = [
          [
            '1',
            '2',
            '3'
          ],
          [
            '4'
          ],
          [
            '5',
            '6',
            '7',
            '8'
          ],
          [
            '9',
            '10'
          ]
        ];

$VAR1 = [
          [
            'a',
            'b',
            'c'
          ],
          [
            'd'
          ],
          [
            'e',
            'f',
            'g',
            'h'
          ],
          [
            'i',
            'j'
          ]
        ];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.