Create an Array Ref in one line in Perl - arrays

I was pondering on the question of whether I could make an array ref in one line in Perl. Sort of like you would define an array. I would normally do the following:
#!/usr/bin/perl
# your code goes here
use warnings;
use strict;
use Data::Dumper;
my #array = qw(test if this works);
my $arrayref = \#array;
print Dumper($arrayref);
My thought was you should be able to just do:
my $arrayref = \(qw(test if this works);
This, however, does not work the way I expected. Is this even possible?

You can do that by using the 'square-bracketed anonymous array constructor' for it. It will create an array reference 'literal'
my $arrayref = [ qw(test if this works) ];
or list every member out:
my $arrayref = [ 'test', 'if', 'this', 'works' ];
You could verify both results with Data Dumper:
$VAR1 = [
'test',
'if',
'this',
'works'
];

If your goal is to create an array reference in one line, use square brackets to create an array reference, which creates an anonymous array.
use Data::Dumper;
my $arrayRef = [qw(test if this works)];
print Dumper($arrayRef);
So if this is what you are looking to do, it is possible.

Related

Extract number from array in Perl

I have a array which have certain elements. Each element have two char "BC" followed by a number
e.g - "BC6"
I want to extract the number which is present and store in a different array.
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
my #band = ("BC1", "BC3");
foreach my $elem(#band)
{
my #chars = split("", $elem);
foreach my $ele (#chars) {
looks_like_number($ele) ? 'push #band_array, $ele' : '';
}
}
After execution #band_array should contain (1,3)
Can someone please tell what I'm doing wrong? I am new to perl and still learning
To do this with a regular expression, you need a very simple pattern. /BC(\d)/ should be enough. The BC is literal. The () are a capture group. They save the match inside into a variable. The first group relates to $1 in Perl. The \d is a character group for digits. That's 0-9 (and others, but that's not relevant here).
In your program, it would look like this.
use strict;
use warnings;
use Data::Dumper;
my #band = ('BC1', 'BC2');
my #numbers;
foreach my $elem (#band) {
if ($elem =~ m/BC(\d)/) {
push #numbers, $1;
}
}
print Dumper #numbers;
This program prints:
$VAR1 = '1';
$VAR2 = '2';
Note that your code had several syntax errors. The main one is that you were using #band = [ ... ], which gives you an array that contains one array reference. But your program assumed there were strings in that array.
Just incase your naming contains characters other than BC this will exctract all numeric values from your list.
use strict;
use warnings;
my #band = ("AB1", "BC2", "CD3");
foreach my $str(#band) {
$str =~ s/[^0-9]//g;
print $str;
}
First, your array is an anonymous array reference; use () for a regular array.
Then, i would use grep to filter out the values into a new array
use strict;
use warnings;
my #band = ("BC1", "BC3");
my #band_array = grep {s/BC(\d+)/$1/} #band;
$"=" , "; # make printing of array nicer
print "#band_array\n"; # print array
grep works by passing each element of an array in the code in { } , just like a sub routine. $_ for each value in the array is passed. If the code returns true then the value of $_ after the passing placed in the new array.
In this case the s/// regex returns true if a substitution is made e.g., the regex must match. Here is link for more info on grep

Self-deleting array elements (once they become undefined)

I have a Perl script generating an array of weak references to objects. Once one of these objects goes out of scope, the reference to it in the array will become undefined.
ex (pseudo code):
# Imagine an array of weak references to objects
my #array = ( $obj1_ref, $obj2_ref, $obj3_ref );
# Some other code here causes the last strong reference
# of $obj2_ref to go out of scope.
# We now have the following array
#array = ( $obj1_ref, undef, $obj3_ref )
Is there a way to make the undefined reference automatically remove itself from the array once it becomes undefined?
I want #array = ($obj1_red, $obj3_ref ).
EDIT:
I tried this solution and it didn't work:
#!/usr/bin/perl
use strict;
use warnings;
{
package Object;
sub new { my $class = shift; bless({ #_ }, $class) }
}
{
use Scalar::Util qw(weaken);
use Data::Dumper;
my $object = Object->new();
my $array;
$array = sub { \#_ }->( grep defined, #$array );
{
my $object = Object->new();
#$array = ('test1', $object, 'test3');
weaken($array->[1]);
print Dumper($array);
}
print Dumper($array);
Output:
$VAR1 = [
'test1',
bless( {}, 'Object' ),
'test3'
];
$VAR1 = [
'test1',
undef,
'test3'
];
The undef is not removed from the array automatically.
Am I missing something?
EDIT 2:
I also tried removing undefined values from the array in the DESTROY method of the object, but that doesn't appear to work either. It appears that since the object is still technically not "destroyed" yet, the weak references are still defined until the DESTROY method is completed...
No, there isn't, short of using a magical (e.g. tied) array.
If you have a reference to an array instead of an array, you can use the following to filter out the undefined element efficiently without "hardening" any of the references.
$array = sub { \#_ }->( grep defined, #$array );
This doesn't copy the values at all, in fact. Only "C pointers" get copied.
Perl won't do this for you automatically. You have a couple of options. The first is to clean it yourself whenever you use it:
my #clean = grep { defined $_ } #dirty;
Or you could create a tie'd array and add that functionality to the FETCH* and POP hooks.

Passing an Array of an Array to a subroutine using perl

Ok, so I got an array of an array (AoA) and I need to pass it to a subroutine, and then access it. This works… but is it strictly correct and indeed is there a better way that I should be doing this?
#!/usr/bin/perl
$a = "deep";
push(#b,$a);
push(#c,\#b);
print "c = #{$c[0]}\n";
&test(\#c);
sub test
{
$d = $_[0];
print "sub c = #{$$d[0]}\n";
}
Thanks
The definitely better way to do it is to use strict; and use warnings; and to declare your variables before using them.
Also, you know that is not good practice to name your vars a or b - give them meaningful names, especially because the $a variable for example, is defined by the compiler (the one used with sort {} #).
use strict; # Always!
use warnings; # Always!
sub test {
my ($d) = #_;
print "$d->[0][0]\n";
# Or to print all the deep elements.
for my $d2 (#$d) {
for (#$d2) {
print "$_\n";
}
}
}
{
my $a = "deep"; # or: my #c = [ [ "deep" ] ];
my #b;
my #c;
push(#b,$a);
push(#c,\#b);
test(\#c); # Can't pass arrays per say, just refs. You had this right.
}
Still needs better names. In particular, $a and $b should be avoided as they can interfere with sort.

How can I create a variable array name in Perl?

Array #p is a multiline array, e.g. $p[1] is the second line.
This code will explain what I want:
$size=#p; # line number of array #p
for($i=0; $i<$size; $i++)
{
#p{$i}= split(/ +/,$p[$i]);
}
I want the result should be like this:
#p0 = $p[0] first line of array #p goes to array #p0;
#p1 = $p[1] second line of array #p goes to array #p1;
...
...
and so on.
But above code does not work, how can I do it?
It is a bad idea to dynamically generate variable names.
I suggest the best solution here is to convert each line in your #p array to an array of fields.
Lets suppose you have a better name for #p, say #lines. Then you can write
my #lines = map [ split ], <$fh>;
to read in all the lines from the file handle $fh and split them on whitespace. The first field of the first line is then $lines[0][0]. The third field of the first line is $lines[0][2] etc.
First, the syntax #p{$i} accesses the entry with the key $i in a hash %p, and returns it in list context. I don't think you meant that. use strict; use warnings; to get warned about undeclared variables.
You can declare variables with my, e.g. my #p; or my $size = #p;
Creating variable names on the fly is possible, but a bad practice. The good thing is that we don't need to: Perl has references. A reference to an array allows us to nest arrays, e.g.
my #AoA = (
[1, 2, 3],
["a", "b"],
);
say $AoA[0][1]; # 2
say $AoA[1][0]; # a
We can create an array reference by using brackets, e.g. [ #array ], or via the reference operator \:
my #inner_array = (1 .. 3);
my #other_inner = ("a", "b");
my #AoA = (\#inner_array, \#other_array);
But careful: the array references still point to the same array as the original names, thus
push #other_inner, "c";
also updates the entry in #AoA:
say $AoA[1][2]; # c
Translated to your problem this means that you want:
my #pn;
for (#p) {
push #pn, [ split /[ ]+/ ];
}
There are many other ways to express this, e.g.
my #pn = map [ split /[ ]+/ ], #p;
or
my #pn;
for my $i ( 0 .. $#p ) {
$pn[$i] = [ split /[ ]+/, $p[$i] ];
}
To learn more about references, read
perlreftut,
perldsc, and
perlref.

Get a random element from an array in a hash

I have seen a lot of results on google on how to get a random array index, but I have not been able to apply it to this scenario.
Consider the following:
my %hash;
my #array = {"foo", "bar", "poo"};
$hash->{mykey} = #array;
How would I get a random element from the array inside $hash->{mykey}? Something like the following code which does not work:
my $element = $hash->{mykey}[rand($hash->{mykey})];
EDIT: So the answers below are extremely informative for this. Compounding my issue in particular was that I was using the threads module, and completely forgot to share the arrays that I was appending to the hash elements! Due to this, the answers were not working for me right away.
After fixing that oversight, the solutions below worked perfectly.
Three errors.
1. The following create an array with one element, a reference to a hash:
my #array = {"foo", "bar", "poo"};
You surely meant to use
my #array = ("foo", "bar", "poo");
2.
$hash->{mykey} = #array;
is the same thing as
$hash->{mykey} = 3;
You can't store arrays in scalars, but you can store a reference to one.
$hash->{mykey} = \#array;
3. It would be
rand(#a) # rand conveniently imposes a scalar context.
for an array, so it's
rand(#{ $ref })
for a reference to an array. That means you want the following:
my $element = $hash->{mykey}[ rand(#{ $hash->{mykey} }) ];
Or you can break it down into two lines.
my $array = $hash->{mykey};
my $element = $array->[ rand(#$array) ];
All together, we have the following:
my #array = ( "foo", "bar", "poo" );
my $hash = { mykey => \#array };
my $element = $hash->{mykey}[ rand(#{ $hash->{mykey} }) ];
I think that your first problem is the construction of your data structure:
#always
use strict;
use warnings;
my %hash;
my #array = ("foo", "bar", "poo");
$hash{mykey} = \#array;
You should probably read perldoc perlreftut to get comfortable with Perl's semantics relating to nested data structures (references).
At this point you can create the structure all at once, which is probably what you mean:
#always
use strict;
use warnings;
my %hash = (
mykey => ["foo", "bar", "poo"],
);
To find the length you just use the regular Perl mechanics for getting the length of the array:
my $length = #{ $hash{mykey} };
and then the random element
my $elem = $hash{mykey}[rand $length];

Resources