Perl - Subroutine argument is another subroutine call - perl

I have a subroutine called grepText, which simply greps a text from another variable. I am trying to split the output. Is it possible to pass the output of grepText as an argument to split directly? without putting the value of grepText in a variable first ? grepText returns a string.
What i am trying to do is:
$output = (split ":", grepText("findThis", $Alltext))[1];
grepText is as follows
sub grepText(){
my #text = split "\n", $_[1];
my $output = grep /$_[0]/, #text;
return $output;
}
it doesn't work. Error is
Too many arguments for main::grepText at a line 115, near "$Alltext)"

It is very much possible to pass the input of a subroutine to any perl function directly without using a perl variable.
I think the issue might be with your "grepText" subroutine. To debug the issue in detail, much more information is required.
I did try your routine and I was able to get the required output:
#!/usr/bin/perl
sub grepText
{
return "hello:world"; # returns a test string
}
my $output = (split ":", grepText($textToFind, $Alltext))[1];
print "$output";
Output:
world

Sure it is. But as you've written it grepText is getting some strange parameters. In
(split ":", grepText(/$textToFind/, $Alltext))[1];
you're calling grepText(/$textToFind/, $Alltext) which is searching for the value of $textToFind in the global variable $_ and, in list context, is inserting either an empty list () or a list containing 1 (1) into the parameters
So you're calling grepText($Alltext) or grepText(1, $Alltext) depending on whether $_ contains the regex pattern in $textToFind
I'm pretty certain that's not what you want to do, so some more information would be nice!
However, whatever grepText returns will be split on colons : and (split ":", grepText(...))[1] will give you the second colon-separated field, which seems to be what you're asking

Related

Assign number to variable in perl subroutine [assign returned list to variables]

I am trying to call a multiple variable from a subroutine but when i try to print, it just print only one variable.
In python the script will be f=mode()[0] and b=mode()[1] and it works.
subroutinefile a.pl
sub mode() {
my ($f, $b);
$f=41;
$b=2;
return ($f,$b);
}
And another file that calls the a.pl
use strict;
use warnings;
require 'a.pl';
my ($f,$b);
$f= mode(0);
$b= mode(1);
print "$f\n";
print "$b\n";
The problem is it only prints 2 for both f and b.
You have passed arguments to subroutine: $f = mode(0); $b = mode(1);.
Try the following:
my ($f,$b) = mode();
Your subroutine returns a fixed length list. You may assign values in the list to your variables.
The problem is the mismatch between the definition of the subroutine (no parameters) and the way you are trying to call it (one argument).
Change those calls to this and it works:
$f= (mode())[0];
$b= (mode())[1];
Think there is a syntactical difference between getting an array index FROM the return set versus sending an argument; function(xxx) sends the argument, (function()[])is using an array slice of the return.

perl call subroutine to generate random strings not working

The script to generate random strings:
sub rand_Strings {
my #chars = ("A".."Z", "a".."z", "0".."9");
my $string;
$string .= $chars[rand #chars] for 1..8;
}
my $strings = &rand_Strings;
print $strings;
However, it works when it is not in a subroutine. And also works if the $string is a global variable. What did I miss? Thanks,
You need to explicitly add a return statement inside your subroutine.
The automatic return of the last statement inside a subroutine does not work inside a loop construction, which in your example is a for loop.
The postfix version of the for loop is equivalent to the regular version with curly braces.
From perldoc perlsub:
If no "return" is found and if the last statement is an expression, its
value is returned. If the last statement is a loop control structure like
a "foreach" or a "while", the returned value is unspecified. The empty sub
returns the empty list.

What is "Use of unitialized value $. in range (or flip)" trying to tell me in Perl

I have the following code snippet in Perl:
my $argsize = #args;
if ($argsize >1){
foreach my $a ($args[1..$argsize-1]) {
$a =~ s/(.*[-+*].*)/\($1\)/; # if there's a math operator, put in parens
}
}
On execution I'm getting "Use of unitialized value $. in range (or flip) , followed by Argument "" isn't numeric in array element at... both pointing to the foreach line.
Can someone help me decipher the error message (and fix the problem(s))? I have an array #args of strings. The code should loop through the second to n't elements (if any exist), and surround individual args with () if they contain a +,-, or *.
I don't think the error stems from the values in args, I think I'm screwing up the range somehow... but I'm failing when args has > 1 element. an example might be:
<"bla bla bla"> <x-1> <foo>
The long and short of it is - your foreach line is broken:
foreach my $a (#args[1..$argsize-1]) {
Works fine. It's because you're using a $ which says 'scalar value' rather than an # which says array (or list).
If you use diagnostics you get;
Use of uninitialized value $. in range (or flip) at
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you
the name of the variable (if any) that was undefined. In some cases
it cannot do this, so it also tells you what operation you used the
undefined value in. Note, however, that perl optimizes your program
and the operation displayed in the warning may not necessarily appear
literally in your program. For example, "that $foo" is usually
optimized into "that " . $foo, and the warning will refer to the
concatenation (.) operator, even though there is no . in
your program.
You can reproduce this error by:
my $x = 1..3;
Which is actually pretty much what you're doing here - you're trying to assign an array value into a scalar.
There's a load more detail in this question:
What is the Perl context with range operator?
But basically: It's treating it as a range operator, as if you were working your way through a file. You would be able to 'act on' particular lines in the file via this operator.
e.g.:
use Data::Dumper;
while (<DATA>) {
my $x = 2 .. 3;
print Dumper $x;
print if $x;
}
__DATA__
line one
another line
third line
fourth line
That range operator is testing line numbers - and because you have no line numbers (because you're not iterating a file) it errors. (But otherwise - it might work, but you'd get some really strange results ;))
But I'd suggest you're doing this quite a convoluted way, and making (potentially?) an error, in that you're starting your array at 1, not zero.
You could instead:
s/(.*[-+*].*)/\($1\)/ for #args;
Which'll have the same result.
(If you need to skip the first argument:
my ( $first_arg, #rest ) = #args;
s/(.*[-+*].*)/\($1\)/ for #rest;
But that error at runtime is the result of some of the data you're feeding in. What you've got here though:
use strict;
use warnings;
my #args = ( '<"bla bla bla">', '<x-1>', '<foo>' );
print "Before #args\n";
s/(.*[-+*].*)/\($1\)/ for #args;
print "After: #args\n";

Perl trim spaces from scalar or array

This is a very basic Perl question but I just want to make sure the actual good practice to it.
Consider I have built a function to trim spaces from strings and I will pass to it either single scalar as string or array of strings, I have this basic working example:
sub trim_spaces {
my (#out) = #_;
for (#out) {
s/\s+//g;
}
return (scalar #out >1)? #out : $out[0];
}
this works in the following calls:
trim_spaces(" These Spaces Are All Removed");
and
#str = (" Str Number 1 ", " Str Number 2 ", " Str Number 3 ");
trim_spaces(#str);
What I am trying to do and understand is the shortest version of this function like this:
sub trim_spaces {
s/\s+//g for (#_);
return #_;
}
This works only if I pass an array:
trim_spaces(#str);
but it does not work if I pass a scalar string:
trim_spaces(" These Spaces Are All Removed");
I understand it should be converted from scalar ref to array, how this can be done in the short version.
Trying to understand the best practices of Perl.
The strict best practice answer to this is to always unpack the contents of #_ into lexical variables, first thing. Perl Best Practices provides the following (paraphrased) arguments:
It's not self-documenting to directly access #_. $_[0], $_[1], and so on tell you nothing about what these parameters are for.
The aliasing behavior of #_ is easily forgotten and can be a source of hard-to-find bugs in a program. Whenever possible, avoid spooky action at a distance.
You can verify each argument while unpacking the #_ array.
And one argument not from PBP:
Seeing my $self = shift; at the beginning of a subroutine clearly marks it as an OO method instead of an ordinary sub.
Sources: Perl Best Practices (Conway 2005), Perl::Critic's relevant policy from PBP.
The elements in #_ are aliases to the original values, which means modifying them inside the subroutine will change them outside as well. The array you're returning is ignored in your examples.
If you store the string in a variable this would work:
my $string = ' These Spaces Are Removed ';
trim_spaces($string); # $string is now 'TheseSpacesAreRemoved'
Or you could use non-destructive substitution and assign the results created by this:
sub trim_spaces { return map { s/\s+//gr } #_ }
my #trimmed = trim_spaces('string one', ' string two');
my ($trimmed_scalar) = trim_spaces('string three');
map will create a list of the values returned by the substitution with the /r flag. The parens around $trimmed_scalar are necessary; see the last example for a version where it isn't.
Alternatively, you could copy the parameters inside the subroutine into lexical variables to avoid action at a distance, which is generally better practice than directly modifying the elements of #_:
sub trim_spaces
{
my #strings = #_;
s/\s+//g for #strings;
return #strings;
}
Personally, I find it nicer when the subroutine returns a value without side effects, and the /r flag saves me the trouble of thinking of a better name for a lexical copy. We can use wantarray to make it smarter in regards to the calling context:
sub trim_spaces
{
return if not defined wantarray;
return map { s/\s+//gr } #_ if wantarray;
return shift =~ s/\s+//gr;
}
On a side note, trim_spaces would be better named remove_whitespace or something similar. Trimming usually means to remove leading and trailing whitespace, and the \s character class matches tabs, newlines, form feeds, and carriage returns in addition to spaces. Use tr/ //dcr to remove just spaces instead if that's what you wanted.

Perl argument parsing into functions when no parentheses are used

I have the following test code:
sub one_argument {
($a) = #_;
print "In one argument: \$a = $a\n";
return "one_argument";
}
sub mul_arguments {
(#a) = #_;
return "mul_argument";
}
print &one_argument &mul_arguments "something", "\n";
My goal is to be able to understand a bit better how perl decides which arguments to go into each function, and to possibly clear up any misunderstandings that I might have. I would've expected the above code to output:
In one argument: mul_argument
one_argument
However, the below is output:
Use of uninitialized value $a in concatenation (.) or string at ./test.pl line 5.
In one argument: $a =
mdd_argument
I don't understand where 'mdd_argument' comes from (Is it a sort of reference to a function?), and why one_argument receives no arguments.
I would appreciate any insight as to how perl parses arguments into functions when they are called in a similar fashion to above.
Please note that this is purely a learning exercise, I don't need the above code to perform as I expected, and in my own code I wouldn't call a function in such a way.
perldoc perlsub:
If a subroutine is called using the & form, the argument list is optional, and if omitted, no #_ array is set up for the subroutine: the #_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid.
In other words, in normal usage, if you use the &, you must use parentheses. Otherwise, the subroutine will be passed the caller's #_.
The mysterious "mdd" is caused because &one_argument doesn't have any arguments and perl is expecting an operator to follow it, not an expression. So the & of &mul_arguments is actually interpreted as the stringwise bit and operator:
$ perl -MO=Deparse,-p -e 'sub mul_arguments; print &one_argument &mul_arguments "something", "\n"'
print((&one_argument & mul_arguments('something', "\n")));
and "one_argument" & "mul_arguments" produces "mdd_argument".