perl print array from subroutine - perl

#! /usr/local/bin/perl
sub getClusters
{
my #clusters = `/qbo/bin/getclusters|grep -v 'qboc33'`;
chomp(#clusters);
return \#clusters;
}
ummm okay .. how do I get at this array to print since ...
foreach $cluster (getClusters())
{ print $cluster."\n"; }
doesn't seem to work.
Thanks.

You are returning a reference, and not dereferencing it anywhere.
foreach $cluster (#{getClusters()})
OR
return #clusters;
Either should fix it (with slightly different effects), with the first one being preferred (your array is kind of big).
You'd use the non-referenced array return for limited number of elements, usually for the purpose of multi-return (thus, usually, limited to 2 or 3, known-length arrays).

If you ran your program under use strict; use warnings;, it would have told you why it failed. As Amadan said, you need to dereference the reference you return.

Perl Solution
#!/usr/local/bin/perl
use strict;
use warnings;
main();
sub main{
{
local $" = "\n";
print "#{getClusters()}";
}
}
sub getClusters{
my #tArray = `/qbo/bin/getclusters|grep -v 'qboc33'`;
chomp #tArray;
return \#tArray;
}
Notice
You don't need a foreach loop for debugging, you can just reset the $" operator however to separate array elements however you like (eg, , , , or how I set it in the code above \n).
Returning an array ref is a plus, don't send back the full array (good job)
use strict/warnings, especially when debugging
try to avoid system calls using ``

To make it easy, you can first receive the return value and then print it like
use strict;
use warning;
my $cluster_array = getClusters();
my #cluster_return = #{$cluster_array};
foreach my $cluster(#cluster_return){
print"$cluster\n";
}

Related

How to check if an array has an element that is not integer?

Firstly, I have a hash:
$hWriteHash{'Identifier'}{'number'} that contains = 1#2#12#A24#48
Then I split this by "#" and then put it in the #arr_of_tenors variable.
Here's the code:
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
use Data::Dumper;
$nums = $hWriteHash{'Identifier'}{'number'};
my #arr_of_tenors = split("#", $nums);
print("#arr_of_tenors\n");
The output is 1 2 12 A24 48
Now, my goal is if the array has an element that's not an integer which is A24, it will go to die function.
Here's what I've tried.
if(not looks_like_number(#arr_of_tenors)){
die "ERROR: Array has an element that's not an integer.";
}else{
print("All good");
}
Obviously, the only acceptable format should be integers only.
I've tried to use looks_like_number but it didn't work. It always goes to else statement.
I know that there is another option which is grep + regex. But as much as possible, I don't want to use regular expressions on this one if there is a function that does the same job. Also, as much as possible, I don't want to iterate each element.
How does looks_like_number works?
Am I missing something?
Here's yet another way:
use Types::Common qw( Int );
if ( Int->all( #arr_of_tenors ) ) {
# all integers
}
else {
# at least one non-integer
}
And another, because why not?
use Types::Common qw( ArrayRef Int );
if ( ArrayRef->of( Int )->check( \#arr_of_tenors ) ) {
# all integers
}
else {
# at least one non-integer
}
How does looks_like_number works? Am I missing something?
It checks one thing at a time, you fed many things (an array). You need to traverse those many things and make a decision.
You want to error out if not all of the elements look like an integer, right? Then you can use notall from the core module List::Util:
use strict;
use warnings;
use List::Util qw(notall);
my $nums = "1#2#12#A24#48"; # $hWriteHash{"Identifier"}{"number"};
my #arr_of_tenors = split("#", $nums);
if (notall { /^-?\d+\z/ } #arr_of_tenors) {
die "ERROR: Array has an element that doesn't look like an integer.";
}
else {
print "All good\n";
}
which dies with
ERROR: Array has an element that doesn't look like an integer.
The notall function performs the mentioned traversal for you and subjects the predicate (the block above) to each element of the list in turn. Returns true if not all of the elements satisfies the condition; false otherwise. It also shortcircuits, i.e., immediately returns true if it sees a noncomplying element.
Noting that i changed looks_like_number to an integer check with a regex as the former accepts more, e.g., 48.7 etc. But if you are sure the incoming values are integer-like, you can replace the regex with looks_like_number($_) in the block above.
You can use List::Util::any to check if any element of the array does not look like a number:
use warnings;
use strict;
use Scalar::Util qw(looks_like_number);
use Data::Dumper;
use List::Util qw(any);
my $sKey = 'abc';
my %hWriteHash;
$hWriteHash{$sKey}{'number'} = '1#2#12#A24#48';
my $nums = $hWriteHash{$sKey}{'number'};
my #arr_of_tenors = split("#", $nums);
print("#arr_of_tenors\n");
if (any { not looks_like_number($_) } #arr_of_tenors) {
die "ERROR: Array has an element that's not an integer.";
}else{
print("All good");
}
print "\n";
From the docs:
Many cases of using grep in a conditional can be written using any
instead, as it can short-circuit after the first true result.
This works with the input you provided. However, looks_like_number will also be true for numbers like 5.37.

Facing issue using map function in perl

I have the below lines:
7290741.out:Info: /test doesn't exist, Running on Network location
7300568.out:sh: /tmp/test/1234_123_test/test1/test2/abc.txt: bad interpreter
I have these lines in an array #test1.
I want to fetch only the numbers before .out and put them in an array #test2.
I used the below code for this:
foreach my $test1(#test1) {
my #test2;
map { /(d+)\.out/ and push #test2, $1 } <$error>;
print "#test2\n";
}
But, when I execute the code, it is printing complete lines, and I want the output like below:
7290741
7300568
Can someone please help?
When I ran your code, I just got 2 blank lines of output.
Regardless, here is a simpler version of your code which just prints out the numbers:
use warnings;
use strict;
my #test1;
while (<DATA>) {
chomp;
push #test1, $_;
}
my #test2;
for (#test1) {
push #test2, $1 if /(\d+)\.out/;
}
for (#test2) {
print "$_\n";
}
__DATA__
7290741.out:Info: /test doesn't exist, Running on Network location
7300568.out:sh: /tmp/test/1234_123_test/test1/test2/abc.txt: bad interpreter
There are numerous problems with your code.
You should use warnings and strict.
The $error variable was not defined in the code you posted.
d in the regular expression should have been \d.
You should have declared the #test2 variable outside the foreach loop; otherwise, it would only have a single value due to variable scoping.
my #nums = map { /^(\d+)\.out/ } <$errors>
Is the simplest way to put it, using map. When you try to first read the errors into an array (#test1) and loop around those values, and inside the loop try to read the values again, you are doing the same thing twice. map is also a loop.
This is assuming that your file with errors is what the filehandle $errors is reading from. Remember also to always use
use strict;
use warnings;
Perhaps OP is looking for solution of following form
Explanation:
<DATA> in this context will represent an array of lines
map will form a loop through lines
regex extracts portion of information OP interested in
result is stored in #test array
NOTE: Data::Dumper is used only for the result visualization
#!/usr/bin/env perl
#
# vim: ai ts=4 sw=4
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my #test = map { /^(.*?).out/ } <DATA>;
say Dumper(\#test);
__DATA__
7290741.out:Info: /test doesn't exist, Running on Network location
7300568.out:sh: /tmp/test/1234_123_test/test1/test2/abc.txt: bad interpreter
Output
$VAR1 = [
'7290741',
'7300568'
];

Perl built in functions as a subroutine reference

I am trying to a set of operations to be performed as an array. For this, I have to pass sub routine references. (There may be other ways to perform this without using an array. But, I feel this is best for now, due to certain other constraints).
Basic sample code for what I am trying to do:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub test()
{
print "Tested\n";
}
my $test;
my #temp = (1, 2, 3);
my $operations = [
[\&test, undef, undef],
[\&shift, \$test, \#temp],
];
foreach(#$operations){
my $func = shift $_;
my $out = shift $_;
$$out = $func->(#$_);
}
print Dumper $test;
Output observed is:
Tested
Undefined subroutine &main::shift called at temp2.pl line 22.
Query - Is it possible to pass built in sub routines as a reference?
There are earlier queries already, reg built in functions as a sub routine reference in here.
As the question was asked about 3 years ealier, was wondering if there is any alternative for it now.
Would appreciate if some one explains why there is a distinction between built in functions and user defined sub routines in this scenario?
shift isn't a sub; it's an operator just like and and +. You'll need to create a sub if you want a reference to a sub.
[sub { shift(#{$_[0]}) }, \$test, \#temp],
Related:
What are Perl built-in operators/functions?
How to get a reference to print?

Directly changing an argument to a subroutine

It would make a lot of things easier in my script if I could use subroutines in the way that shift, push, and other built-in subroutines work: they can all directly change the variable that is passed to it without the need to return the change.
When I try to do this the variable is copied at some point and I appear to be simply changing the copy. I understand that this would be fine with references but it even happens with arrays and hashes, where I feel like I am simply passing the variable I was working on to the sub so that more work can be done on it:
#it = (10,11);
changeThis(#it);
print join(" ", #it),"\n"; #prints 10 11 but not 12
sub changeThis{
$_[2] = 12;
}
Is there a way to do this? I understand that it isn't best practice, but in my case it would be very convenient.
That's what prototypes are for:
#!/usr/bin/perl
use strict;
use warnings;
sub changeThis(\#); # the argument will be seen as an array ref (prototype must come before the call!)
my #it = (10,11);
changeThis #it; # even when called with an array
print join(" ", #it),"\n"; #prints 10 11 12
sub changeThis(\#)
{ my( $ar)= #_; $ar->[2]= 12; }
See http://perldoc.perl.org/perlsub.html#Prototypes for more information.
It's not really a popular method though, passing actual array references is probably a better alternative, with less magic involved.
The problem is that the sub call expands the variable to a list of values, which are passed on to the sub routine. I.e. a copy is passed, not the variable itself. Your sub call is equal to:
changeThis(11, 12);
If you wish to change the original array, pass a reference instead:
use strict;
use warnings;
my #it = (10,11);
changeThis(\#it);
print join(" ", #it),"\n";
sub changeThis{
my $array = shift;
$$array[2] = 12;
}
Also, #_[2] will give you the warning:
Scalar value #_[2] better written as $_[2]
If you use warnings, which of course you should. There is no good reason to not turn on warnings and strict, unless you know exactly what you are doing.
As the previous answers suggest, you should use a reference passed to the subroutine.
Additionally you also can use implicit referencing if you want to read trough the documentation for Prototypes
sub changeThis(\#);
#it = (10,11);
changeThis #it;
print join(" ", #it),"\n"; #prints 10 11 12
sub changeThis(\#){
$_[0][2] = 12;
}
(note that you either have to predeclare your subs before the first call or put the sub definitions on top.)

How would I do the equivalent of Prototype's Enumerator.detect in Perl with the least amount of code?

Lately I've been thinking a lot about functional programming. Perl offers quite a few tools to go that way, however there's something I haven't been able to find yet.
Prototype has the function detect for enumerators, the descriptions is simply this:
Enumerator.detect(iterator[, context]) -> firstElement | undefined
Finds the first element for which the iterator returns true.
Enumerator in this case is any list while iterator is a reference to a function, which is applied in turn on each element of the list.
I am looking for something like this to apply in situations where performance is important, i.e. when stopping upon encountering a match saves time by disregarding the rest of the list.
I am also looking for a solution that would not involve loading any extra module, so if possible it should be done with builtins only. And if possible, it should be as concise as this for example:
my #result = map function #array;
You say you don't want a module, but this is exactly what the first function in List::Util does. That's a core module, so it should be available everywhere.
use List::Util qw(first);
my $first = first { some condition } #array;
If you insist on not using a module, you could copy the implementation out of List::Util. If somebody knew a faster way to do it, it would be in there. (Note that List::Util includes an XS implementation, so that's probably faster than any pure-Perl approach. It also has a pure-Perl version of first, in List::Util::PP.)
Note that the value being tested is passed to the subroutine in $_ and not as a parameter. This is a convenience when you're using the first { some condition} #values form, but is something you have to remember if you're using a regular subroutine. Some more examples:
use 5.010; # I want to use 'say'; nothing else here is 5.10 specific
use List::Util qw(first);
say first { $_ > 3 } 1 .. 10; # prints 4
sub wanted { $_ > 4 }; # note we're using $_ not $_[0]
say first \&wanted, 1 .. 10; # prints 5
my $want = \&wanted; # Get a subroutine reference
say first \&$want, 1 .. 10; # This is how you pass a reference in a scalar
# someFunc expects a parameter instead of looking at $_
say first { someFunc($_) } 1 .. 10;
Untested since I don't have Perl on this machine, but:
sub first(\&#) {
my $pred = shift;
die "First argument to "first" must be a sub" unless ref $pred eq 'CODE';
for my $val (#_) {
return $val if $pred->($val);
}
return undef;
}
Then use it as:
my $first = first { sub performing test } #list;
Note that this doesn't distinguish between no matches in the list and one of the elements in the list being an undefined value and having that match.
Just since its not here, a Perl function definition of first that localizes $_ for its block:
sub first (&#) {
my $code = shift;
for (#_) {return $_ if $code->()}
undef
}
my #array = 1 .. 10;
say first {$_ > 5} #array; # prints 6
While it will work fine, I don't advocate using this version, since List::Util is a core module (installed by default), and its implementation of first will usually use the XS version (written in C) which is much faster.