3

I want to create 10 one dimensional arrays , and put these 10 one dimensional arrays to another one dimensional array , and store some data to some specific index of array .

but the output what I expect should be

expect output         real output
    0                      1
    1                      1
    0                      1
    3                      1
    0                      1
    5                      1
    0                      1
    7                      1
    0                      1
    0                      1

here is my code

@Hits = ();

# Create 10 one dimension array
for($i=0;$i<=9;$i++)
{
    @Space = ();
    push(@Hits,\@Space);
}

# Store some data to some index
push(@{$Hits[1]},1);
push(@{$Hits[3]},3);
push(@{$Hits[5]},5);
push(@{$Hits[7]},7);

# print the first element of 10 arrays
for($i=0;$i<=9;$i++)
{
    print $Hits[$i]->[0];
    print "\n";
}

thanks

3 Answers 3

7

The Problem is that you aren't properly declaring your variables. For every script, you should

use strict; use warnings;

This disallows common error sources, warns about iffy stuff, and forces you to properly declare all your variables.

By default, all undeclared variables are considered global. Therefore, in

for($i=0;$i<=9;$i++)
{
    @Space = ();
    push(@Hits,\@Space);
}

the @Space refers to the same array in each iteration. Ergo, all ten entries in @Hits are a reference to the same array. Let's inspect what @Hits actually is. We can do so with Data::Dumper or the Data::Dump module (the latter usually produces prettier output):

use Data::Dump;  # use Data::Dumper;
dd \@Hits;       # print Dumper \@Hits;

We get with Data::Dumper (easier to understand):

$VAR1 = [
          [
            1,
            3,
            5,
            7
          ],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0],
          $VAR1->[0]
        ];

So I said the solution would be declaring your variables. Specifically, we want lexical variables. These variables are only visible inside the block where they are declared. This makes reasoning about code much easier. We can declare a lexical variable like so:

my $foo = 123;

When we have a loop like

my @Hits;
for my $i  (0 .. 9) {
  my @Space;
  push @Hits, \@Space;
}

then each time the my is executed, we get a new @Space. Oh, and I have used a foreach-loop, that iterates over the range 0 .. 9 with the (lexical) $i variable. I find this easier to understand than the C-style loops you used.

Because every element in @Hits now is a different arrayref, we get the expected data structure. As Data::Dump output:

[[], [1], [], [3], [], [5], [], [7], [], []]

When we now execute your loop that prints out the first value of each sub-array, then you may be suprised by the empty lines in between the numebers. This is because e.g. the first arrayref does not have an entry at index 0, and therefore returns the special undef value. When used as a string, this is the empty string. If you followed my advice and did the use warnings, you also get a message that you are printing an uninitialized value.

We can solve this by testing for definedness, and providing a zero otherwise. Since perl5 v10, we can use the defined-or operator // for this (on earlier perls, the || logical or has to do).

for my $i (0 .. 9) {
  my $value = $Hits[$i][0] // 0;
  print "$value\n";
}

There are a few other bits we can improve:

  • We don't have to manually create the @Space arrays; Perl does this behind the scenes when you dereference an array entry like @{ $Hits[$i] }. This is called autovivification.
  • Not only can we iterate over ranges, but also over arrays. This is much better than hardcoding indices.
  • Since v10, you can use the say feature. The say function is exactly like print but appends a newline at the end.

Here is how I'd have written that code:

#!/usr/bin/perl
use strict; use warnings; use feature 'say';

my @Hits;

for my $i (1, 3, 5, 7) {
  push @{ $Hits[$i] }, $i;
}

for my $arrayref (@Hits) {
  say $arrayref->[0] // 0;
}

Output:

0
1
0
3
0
5
0
7

(Note that we never initialized values at positions 8 and 9, so they are not shown. We could amend this by iterating over the slice @Hits[0 .. 9].)

Sign up to request clarification or add additional context in comments.

Comments

1

I change your code like following:

#! /usr/bin/perl -w
@Hits = ();

push(@{$Hits[1]},1);
push(@{$Hits[3]},3);
push(@{$Hits[5]},5);
push(@{$Hits[7]},7);

#print the content of
for($i=0;$i<=9;$i++)
{
    if (defined ($Hits[$i])) {
        print "$Hits[$i][0]\n";
    } else {
        print "0\n";
    }
}

It's incorrect to give the ref of @space to @Hits and that makes the wrong result. It's not necessary to initial @Hits in perl.

Comments

1
perl -e "use Data::Dump; @sf=(); push @{$sf[0]},"0"; push @{$sf[1]},"1"; dd \@sf;"
[[0], [1]]

or

perl -e "use Data::Dump; @sf=(); push @sf,["0"]; push @sf,["1"]; dd \@sf;"
[[0], [1]]

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.