1

I am building five arrays in perl like (null, null, 32, 54, null, 59). So I first put the values that I know at specific index and fill the others with null. I do this like this

my @p0_series, @p1_series;
$p0_series[2] = "test";
$p1_series[3] = "string";
$p2_series[2] = "hello";
$p3_series[2] = "hi";
for($a = 0; $a < 5; $a++) {
  if(!defined $p0_series[$a]) {
   $p0_series[$a] = null;
  }

  if(!defined $p1_series[$a]) {
   $p1_series[$a] = null;
  }

  if(!defined $p3_series[$a]) {
   $p3_series[$a] = null;
  }

  if(!defined $p4_series[$a]) {
   $p4_series[$a] = null;
  }

  if(!defined $p5_series[$a]) {
   $p5_series[$a] = null;
  }
}

I am trying to reduce this code to simpler one but I am not able to use the variable name p0_series, p1_seires dynamically in a loop. I tried like

for($a=0: $a <5; $a++ ){
if(!defined $p$i_series[$a] ) {
 #  assign values;
}
}

Which is not working. I am new to perl any help is appreciated. Is it possible I can assign the null value to all the undef elements in the array in a simpler manner?

1
  • Tip: use strict; use warnings at the top of your script to get warned about some problems with your script which I didn't address in my answer. Commented Dec 31, 2013 at 11:22

2 Answers 2

4

Perl doesn't have null, but it has undef – which incidentally is the default value for unassigned elements in arrays:

my @array = (undef, undef, 32, 54, undef, 59);

creates the same structure as:

my @array;
$array[2] = 32;
$array[3] = 54;
$array[5] = 59;

There is no need to assign the other fields to undef yourself.

If you are trying to assign a default value to elements which are currently undef, you could write something like this (but note the limitations below):

defined or $_ = "default" for @array;

On Perl 5.10 or later, you can use the // defined-or operator:

$_ //= "default" for @array

To assign defaults for multiple arrays:

$_ //= "default" for @array, @other_array

If you want to set an array to a specific length, you can do $#array = $length - 1, so this actually specifies the highest index. This removes entries from longer arrays. For shorter arrays, the newly created entries will all be undef.


There is a small problem with this: Perl has two kinds of undef:

  • Scalars can contain the value “undef” like my $foo = undef.

    This is the case if we initialize the whole array at once like my @array = (undef, undef, 32, 54, undef, 59).

  • Unassigned values in arrays or hashes share their undef scalar, which is read-only.

    This is the case when we initialize the array by assigning some indices only, like $array[4] = 2.

Usually this is no problem, but in a for-loop, the $_ is an alias to the current scalar, which in our case can be readonly. Therefore we can't always do $_ //= "default" for @array but must either:

  • Assign directly into the array element, which creates an assignable scalar in that slot:

    $array[$_] //= "default" for 0 .. $#array;
    

    For multiple arrays:

    for my $ref (\@array, \@other_array) {
      $ref->[$_] //= "default" for 0 .. $#$ref;
    }
    
  • Don't assign to the element and create a copy of the array with defaults instead:

    @array = map { $_ // "default" } @array;
    

    For multiple arrays:

    @$_ = map { $_ // "default" } @$_ for \@array, \@other_array;
    

    While I perceive this as more elegant, there can be some problems with this as each array element is copied.

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

7 Comments

Awesome, great tip. My requirement is for undef elements I need to make it null. I have to feed this array elements to highcharts so null is required there. This is exactly I was searching for.
Hi, I am using perl v5.10.1. I am able to change for single array but when I do if for multiple arrays I get the error "Modification of a read-only value attempted at - line " .
@SpreeTheGr8 Could you show the code that isn't working? You can only assign defaults for variables like $_ = 1 for @array, not for literals like $_ = 1 for 1, 2, 3.
my @test; my @test2; $test[3] = 'test'; $test2[2] = 'samp'; $_ //= 'a' for @test, @test2;
@SpreeTheGr8 Thank you, I updated with a definitive solution. This error was triggered by the way (a) how unassigned array values are represented and (b) how for-loops work. Very subtle stuff and not my favorite Perl feature.
|
-2

Agreed, but just incase in perl you can also create run-time variable name. As in your case

if(!defined $p$i_series[$a] )

if (!defined ${"p$i"."_series"}[$a] )

This should do the job and create the variable $p1_series[$a] , $p2_series[$a] and further on

1 Comment

-1: This is a horrible tip and recommends bad programming practices which obfuscate the code and make it less maintainable. Your “symbolic references” also only work with global variables, which are a problem themselves. I recommend you read MJD's three-part series on the issue. His solution: Use a hash (of arrayrefs).

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.