How do you access function parameters in Perl? - perl

In C++ I would do something like this:
void some_func(const char *str, ...);
some_func("hi %s u r %d", "n00b", 420);
In PHP I would do like this:
function some_func()
{
$args = func_get_args();
}
some_func($holy, $moly, $guacomole);
How do I do that in Perl?
sub wut {
# What goes here?
}

You would do:
sub wut {
my #args = #_;
...
}
Perl automatically populates the special #_ variable when you call a function. You can access it in multiple ways:
directly, by simply using #_ or individual elements within it as $_[0], $_[1], and so on
by assigning it to another array, as shown above
by assigning it to a list of scalars (or possibly a hash, or another array, or combinations thereof):
sub wut {
my ( $arg1, $arg2, $arg3, #others ) = #_;
...
}
Note that in this form you need to put the array #others at the end, because if you put it in earlier, it'll slurp up all of the elements of #_. In other words, this won't work:
sub wut {
my ( $arg1, #others, $arg2 ) = #_;
...
}
You can also use shift to pull values off of #_:
sub wut {
my $arg1 = shift;
my $arg2 = shift;
my #others = #_;
...
}
Note that shift will automatically work on #_ if you don't supply it with an argument.
Edit: You can also use named arguments by using a hash or a hash reference. For example, if you called wut() like:
wut($arg1, { option1 => 'hello', option2 => 'goodbye' });
...you could then do something like:
sub wut {
my $arg1 = shift;
my $opts = shift;
my $option1 = $opts->{option1} || "default";
my $option2 = $opts->{option2} || "default2";
...
}
This would be a good way to introduce named parameters into your functions, so that you can add parameters later and you don't have to worry about the order in which they're passed.

Related

Can I reference a named subroutine with some arguments

I have a subroutine taking multiple arguments and want to make a reference to it with one of the arguments set, so that the reference takes one argument less. Optimal case would be
my $subref = \&routine($arg1);
...
my $result = $subref->($arg2,$arg3);
In perlref there is an example with an anonymous subroutine like this, however I cannot get the equivalent with a named one working.
Below is a full fledged example of what I mean. While $func (ref to anonymous sub) and $func2 (ref to named sub, but without arguments) work. $func3 gives the error "Not a CODE reference[...]".
Have I missed something or is this actually impossible?
use strict;
use warnings;
sub args{
my $arg1 = (shift or "none");
my $arg2 = (shift or "none");
my $arg3 = (shift or "none");
my (undef, undef, undef, $function) = caller(0);
return "me: $function\narg1 = $arg1\narg2 = $arg2\narg3 = $arg3\n";
}
sub just_a_ref {
return \&args;
}
sub new_arg_anon {
my $arg = shift;
return sub{
my $arg1 = $arg;
my $arg2 = (shift or "none");
my $arg3 = (shift or "none");
my (undef, undef, undef, $function) = caller(0);
return "me: $function\narg1 = $arg1\narg2 = $arg2\narg3 = $arg3\n";
}
}
sub new_arg {
my $arg = shift;
return \&args($arg);
}
my $func = new_arg_anon("one");
print $func->("two","three"); #works fine
my $func2 = just_a_ref();
print $func2->("un", "deux", "trois"); #works fine
my $func3 = new_arg("eins");
print $func3->("zwei", "drei"); #Not a CODE reference
You have to create a new anonymous function that does exactly that. Call the target function with one argument set and passing the rest of the arguments to it. In your example the new_arg function should be:
sub new_arg {
my $arg = shift;
return sub {args($arg, #_)};
}
\&args($arg) is \( args($arg) ), that is, a reference to the return value of the function call args($arg), not a reference to the function args called with the argument $arg.
print $func3; # SCALAR(0x8000a1a50)
To make a reference to a function that executes the args subroutine with $arg as the first argument, use
sub new_arg {
my $arg = shift;
return sub { args($arg,#_) };
}
(look at that, just like Georg Mavridis's answer)

How can I do function partial application in Perl?

Is there any way to achieve partial application in Perl?
Suppose, I want to do something like:
sub each_file($arr, $op) {
$op->($_) for #{$arr};
...
}
sub each_line($op, $file) {
...
}
each_file($arr, each_line($op));
I want to partially apply each_line() to only $op, so it'll become a new function can be passed to $each_file, how do I express this in idiomatic Perl?
You can do this in Perl with two approaches combined:
A function which returns a function reference
Closures
Example:
sub each_file {
my ($arr, $line_fn) = #_;
$line_fn->($_) for #{$arr};
...
}
sub each_line {
my ($op, $file) = #_;
...
}
sub make_line_processor {
my ( $op ) = #_;
# This is closed over $op, which effectively becomes
# a constant for the returned function
my $fn = sub {
return each_line( $op, #_ );
};
return $fn;
}
# To call it:
each_file( $arr, make_line_processor($op) );
This can be an even more useful technique in cases where you don't want $op directly, but some expensive-to-fetch derivation of it. In which case you would calculate the derived value just once (in the make_line_processor function) and close over that instead.
# given some $op as implied by your code snippet
each_file($arr, sub { each_line($op, shift) });
# shift op will be applied when anonymous sub { … } is called
(Your code snippet doesn't make it entirely clear what you intend $op to be when you make the call to each_line. It's usually better to present small working programs.)
You can roll this functionality up into a class. Then you can overload the subroutine dereference operator to make it look like your class is really a code reference.
package Partial;
use overload '&{}' => \&call;
sub new {
my $class = shift;
my $code = shift;
bless {code => $code, args => \#_}, $class;
}
sub call {
my ($self) = #_;
return sub{ $self->{code}->(#{$self->{args}}, #_) }
}
You can then use it like this:
sub printArgs {
print join ", ", #_;
print "\n";
}
my $partial = Partial->new(\&printArgs, 'foo', 'bar');
$partial->('baz', 'bat');
# prints foo, bar, baz, bat

Passing one subroutine to another subroutine

I have one function sub _where(\# \&) which takes 2 arguments: the first is an array, and the second should be another function. This other function returns a boolean value, and I want to call it inside my for loop of sub _where(\# \&) function.
I am having trouble extracting the function I am passing in into a custom local name. I think I do need some local name for it, because it should be possible to pass different boolean functions to my where function.
where:
sub _where(\# \&)
{
my #stud = #{$_[0]};
my $student;
my $function = shift;
my $bool = 0;
my $i;
for $i(0..$#stud)
{
my $student = $stud[$i];
function $student;
}
}
Function1 that should be passed:
sub name_starts_with($)
{
my $letter = 'B';
my $student = shift;
my $first;
$first = substr($student -> name, 0, 1);
if($first eq $letter)
{
return 1;
}
}
Function2 that should be passed to where:
sub points_greater_than($)
{
my $sum_pts = 5;
my $student = shift;
my $pts;
$pts = $student -> points;
if($pts > $sum_pts)
{
return 1;
}
}
Hope you guys could help me out here. Cheers
You shouldn't use prototypes. They work differently in Perl from other languages and are almost never a good choice.
You should also avoid making a local copy of the passed-in array unless you want to modify it without affecting the external data.
Finally, a subroutine name beginning with an underscore usually indicates that it is a private method of a class. It doesn't look like that's the case here.
Your code should look like this
sub _where {
my ($stud, $function) = #_;
my $student;
my $bool = 0;
for my $i (0..$#stud) {
my $student = $stud->[$i];
$function->($student);
}
}
Then you can call it as
_where(\#student, \&function);
One problem is in how you get parameters:
my #stud = #{$_[0]}; # <-- this doesn't remove first parameter from list
my $student;
my $function = shift; # <-- therefore you'll still get first parameter, not second
Try this fix:
my $function = $_[1]; # always get second parameter
Update
Adding example of how to pass reference to function into other function:
_where(\#stud, \&name_starts_with);
You seem to be trying to write another language in Perl. Ick. Try this:
sub _where
{
my $students = shift;
my $function = shift;
$function->($_) for #$students;
}
sub name_starts_with
{
my $student = shift;
my $letter = 'B';
my $first = substr($student->name, 0, 1);
return $first eq $letter; # same as 'return $first eq $letter ? 1 : undef;'
}
sub points_greater_than
{
my $student = shift;
my $sum_pts = 5;
my $pts = $student->points;
return $pts > $sum_pts;
}
And you would call it like _where(\#students, \&name_starts_with).
But I'm not exactly what the purpose of your _where function is, as it does not return anything (except the last statement evaluated, which doesn't seem too useful in this context).
Maybe you just want grep?
my #students_b = grep { substr($_->name, 0, 1) eq 'B' } #students;
You have bug in argument handling in function _where. You are putting array reference into $function variable. You have to do
my #stud = #{shift()};
my $student;
my $function = shift();
or
my #stud = #{$_[0]};
my $student;
my $function = $_[1];
or which I would prefer
sub _where(\# \&)
{
my ($stud, $function) = #_;
for my $student (#$stud)
{
$function->($student);
}
}
but don't mix those methods.
After you fix the problem with grabbing the first argument, here are three ways to call a subroutine from a code reference:
&$function($student); # uses the fewest characters!
&{$function}($student); # the style you're using for the array ref
$function->($student); # my favorite style
You can find a lot more detailed information by reading the perlref man page.
If you change the order of the arguments so that the coderef is first, your code will be a little bit more Perlish.
sub _where(\&#){
my $func = shift;
my #return;
for(#_){
push #return, $_ if $func->($_);
}
return #return;
}
If you were well versed in Perl, you would notice that I just re-implemented grep (poorly).
sub name_starts_with{
'B' eq substr($_->name, 0, 1);
}
sub points_greater_than{
$_->points > 5;
}
my #b_students = _where( &name_starts_with, #students );
my $count_of_students_above_5 = _where( &points_greater_than, #students );
Since those subroutines now rely on $_, we should just use grep.
my #b_students = grep( &name_starts_with, #students );
my $count_of_students_above_5 = grep( &points_greater_than, #students );
Since those subroutines are also very short, how about just using a block.
my #b_students = grep {
'B' eq substr($_->name, 0, 1)
} #students;
my $count_of_students_above_5 = grep {
$_->points > 5;
} #students;

Passing variables to a Perl subroutine

I would like to pass the following variables to subroutine mySubroutine, $name, $age and then this multidimensional array:
$name = "jennifer";
$age = 100;
$list[0][0] = "TEST NAME 2";
$list[0][1] = "TEST GROUP 2";
$[0][2] = 10;
$[1][0] = "TEST NAME 2";
$[1][1] = "TEST GROUP 2";
$[1][2] = 2;
Subroutine:
sub mySubroutine
{
}
I have tried $_[0], and #_, but I don't seem to get the variables passed to the subroutine correctly.
There are several ways to do it (like most things in Perl). I personally do it like this:
sub mySubroutine
{
# Get passed arguments
my ($name, $age, $refList) = #_;
# Get the array from the reference
my #list = #{$refList};
# Good to go
}
# You need to pass #list as reference, so you
# put \#list, which is the reference to the array
mySubroutine($name, $age, \#list);
Another option, as long as you are only passing one array, is to pass it normally by value as the last element:
sub scalars_and_one_array {
my $name = shift;
my $age = shift;
foreach my $element (#_)
{
# Do something with the array that was passed in.
}
}
scalars_and_one_array($name,$age,#array);
However, it is most efficient to avoid any additional copy of the array by only using a reference within the sub. This does mean that changes to the array in the sub affect the original, however:
sub array_by_ref {
my $array_ref = shift;
foreach my $element (#$array_ref)
{
# Changing $element changes #original_array!
}
}
array_by_ref(\#original_array);
Another way, which passes the array by reference, but then makes a copy of it to avoid changing the original when you edit it.
sub mySubroutine
{
## Retrieve name
my $name = shift;
## Retrieve age
my $age = shift;
## Retrieve list reference
my $refList = shift;
## De-reference the list's scalar
my #list = #{$refList};
# Good to go
}
## Function call
mySubroutine($name, $age, \#list);
For a better understanding, please refer to perlsub (Perl subroutines).

How to append some logic before a function using Test::MockModule?

This is the mock module I'm using:
http://metacpan.org/pod/Test::MockModule
How to mock sub a to sub b,
where sub b just does something else before call sub a?
sub b {
#do something else
a(#_);
}
You can grab the un-mocked method with can ( UNIVERSAL::can ). After that you can either goto it or just use the ampersand calling style to pass the same arguments. That's what I did below.
my $old_a = Package::To::Be::Mocked->can( 'a' );
$pkg->mock( a => sub {
# do some stuff
&$old_a;
});
This of course assumes that your sub isn't AUTOLOAD or generated through AUTOLOAD without redefining can. (I learned years back that if you're going to mess with AUTOLOAD, it's probably best to do the work in can.)
You could also create your own utility that does this automatically, by invading modifying the Test::MockModule's namespace.
{ package Test::MockModule;
sub modify {
my ( $self, $name, $modfunc ) = #_;
my $mock_class = $self->get_package();
my $old_meth = $mock_class->can( $name );
croak( "Method $name not defined for $mock_class!" ) unless $old_meth;
return $self->mock( $name => $modfunc->( $old_meth ));
}
}
And you could call it like so:
$mock->modify( a => sub {
my $old_a = shift;
return sub {
my ( $self ) = #_;
# my stuff and I can mess with $self
local $Carp::CarpLevel += 1;
my #returns = &$old_a;
# do stuff with returns
return #returns;
};
});