Passing one subroutine to another subroutine - perl

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;

Related

Making iteration recursive using PERL

Bon,
I have this code which is a simple loop, which works… to call it you need to send it a reference to an array of numbers.
#blah = (0b0010,0b010,0b0111);
$ans = &logical_loop(\#blah);
sub logical_loop()
{
my $codes = $_[0];
my $ans = 0;
foreach (#$codes) {
printf "%b\n",$_;
$ans = ($ans | $_)
}
return($ans);
}
I wanted to make it recursive, so I wrote this code which doesn't work…. please can somebody tell me what I have missed? something to do with variables scopes perhaps?
sub recursive_loop
{
my $codes = $_[0];
my $el = shift #$codes;
if (#$codes == ()) {
return ($el | $answer);
} else {
$answer = (&recursive_loop(\#$codes) | $el);
}
}
sub recursive_loop {
return 0 unless #_;
my $head = shift;
return $head | recursive_loop(#_);
}
#blah = (0b0010,0b010,0b0111);
recursive_loop(#blah);
More efficient tail recursive:
sub or_it_up {
unshift #_, 0;
goto &recursive_loop;
}
sub recursive_loop {
my $ans = shift;
return $ans unless #_;
unshift #_, $ans | shift;
goto &recursive_loop;
}
#blah = (0b0010,0b010,0b0111);
or_it_up(#blah);
You can use calling recursive_loop as function but in this way it will not make stack frame.
You can also write it simply without or_it_up which serves just educational purpose. Calling recursive_loop directly will make result caused by nature of binary or.
I see a few problems with your subroutine.
its name contains a spacing mark
it doesn't call itself therefore no recursion is possible

functional Perl: Filter, Iterator

I have to write Perl although I'm much more comfortable with Java, Python and functional languages. I'd like to know if there's some idiomatic way to parse a simple file like
# comment line - ignore
# ignore also empty lines
key1 = value
key2 = value1, value2, value3
I want a function that I pass an iterator over the lines of the files and that returns a map from keys to list of values. But to be functional and structured I'd like to:
use a filter that wraps the given iterator and returns an iterator without empty lines or comment lines
The mentioned filter(s) should be defined outside of the function for reusability by other functions.
use another function that is given the line and returns a tuple of key and values string
use another function that breaks the comma separated values into a list of values.
What is the most modern, idiomatic, cleanest and still functional way to do this? The different parts of the code should be separately testable and reusable.
For reference, here is (a quick hack) how I might do it in Python:
re_is_comment_line = re.compile(r"^\s*#")
re_key_values = re.compile(r"^\s*(\w+)\s*=\s*(.*)$")
re_splitter = re.compile(r"\s*,\s*")
is_interesting_line = lambda line: not ("" == line or re_is_comment_line.match(line))
and re_key_values.match(line)
def parse(lines):
interesting_lines = ifilter(is_interesting_line, imap(strip, lines))
key_values = imap(lambda x: re_key_values.match(x).groups(), interesting_lines)
splitted_values = imap(lambda (k,v): (k, re_splitter.split(v)), key_values)
return dict(splitted_values)
A direct translation of your Python would be
my $re_is_comment_line = qr/^\s*#/;
my $re_key_values = qr/^\s*(\w+)\s*=\s*(.*)$/;
my $re_splitter = qr/\s*,\s*/;
my $is_interesting_line= sub {
my $_ = shift;
length($_) and not /$re_is_comment_line/ and /$re_key_values/;
};
sub parse {
my #lines = #_;
my #interesting_lines = grep $is_interesting_line->($_), #lines;
my #key_values = map [/$re_key_values/], #interesting_lines;
my %splitted_values = map { $_->[0], [split $re_splitter, $_->[1]] } #key_values;
return %splitted_values;
}
Differences are:
ifilter is called grep, and can take an expression instead of a block as first argument. These are roughly equivalent to a lambda. The current item is given in the $_ variable. The same applies to map.
Perl doesn't emphazise laziness, and seldomly uses iterators. There are instances where this is required, but usually the whole list is evaluated at once.
In the next example, the following will be added:
Regexes don't have to be precompiled, Perl is very good with regex optimizations.
Instead of extracting key/values with regexes, we use split. It takes an optional third argument that limits the number of resulting fragments.
The whole map/filter stuff can be written in one expression. This doesn't make it more efficient, but emphazises the flow of data. Read the map-map-grep from bottom upwards (actually right to left, think of APL).
.
sub parse {
my %splitted_values =
map { $_->[0], [split /\s*,\s*/, $_->[1]] }
map {[split /\s*=\s*/, $_, 2]}
grep{ length and !/^\s*#/ and /^\s*\w+\s*=\s*\S/ }
#_;
return \%splitted_values; # returning a reference improves efficiency
}
But I think a more elegant solution here is to use a traditional loop:
sub parse {
my %splitted_values;
LINE: for (#_) {
next LINE if !length or /^\s*#/;
s/\A\s*|\s*\z//g; # Trimming the string—omitted in previous examples
my ($key, $vals) = split /\s*=\s*/, $_, 2;
defined $vals or next LINE; # check if $vals was assigned
#{ $splitted_values{$key} } = split /\s*,\s*/, $vals; # Automatically create array in $splitted_values{$key}
}
return \%splitted_values
}
If we decide to pass a filehandle instead, the loop would be replaced with
my $fh = shift;
LOOP: while (<$fh>) {
chomp;
...;
}
which would use an actual iterator.
You could now go and add function parameters, but do this only iff you are optimizing for flexibility and nothing else. I already used a code reference in the first example. You can invoke them with the $code->(#args) syntax.
use Carp; # Error handling for writing APIs
sub parse {
my $args = shift;
my $interesting = $args->{interesting} or croak qq("interesting" callback required);
my $kv_splitter = $args->{kv_splitter} or croak qq("kv_splitter" callback required);
my $val_transform= $args->{val_transform} || sub { $_[0] }; # identity by default
my %splitted_values;
LINE: for (#_) {
next LINE unless $interesting->($_);
s/\A\s*|\s*\z//g;
my ($key, $vals) = $kv_splitter->($_);
defined $vals or next LINE;
$splitted_values{$key} = $val_transform->($vals);
}
return \%splitted_values;
}
This could then be called like
my $data = parse {
interesting => sub { length($_[0]) and not $_[0] =~ /^\s*#/ },
kv_splitter => sub { split /\s*=\s*/, $_[0], 2 },
val_transform => sub { [ split /\s*,\s*/, $_[0] ] }, # returns anonymous arrayref
}, #lines;
I think the most modern approach consists in taking advantage of the CPAN modules. In your example, Config::Properties may helps:
use strict;
use warnings;
use Config::Properties;
my $config = Config::Properties->new(file => 'example.properties') or die $!;
my $value = $config->getProperty('key');
As indicated in the posts linked to by #collapsar, Higher-Order Perl is a great read for exploring functional techniques in Perl.
Here is an example that hits your bullet points:
use strict;
use warnings;
use Data::Dumper;
my #filt_rx = ( qr{^\s*\#},
qr{^[\r\n]+$} );
my $kv_rx = qr{^\s*(\w+)\s*=\s*([^\r\n]*)};
my $spl_rx = qr{\s*,\s*};
my $iterator = sub {
my ($fh) = #_;
return sub {
my $line = readline($fh);
return $line;
};
};
my $filter = sub {
my ($it,#r) = #_;
return sub {
my $line;
do {
$line = $it->();
} while ( defined $line
&& grep { $line =~ m/$_/} #r );
return $line;
};
};
my $kv = sub {
my ($line,$rx) = #_;
return ($line =~ m/$rx/);
};
my $spl = sub {
my ($values,$rx) = #_;
return split $rx, $values;
};
my $it = $iterator->( \*DATA );
my $f = $filter->($it,#filt_rx);
my %map;
while ( my $line = $f->() ) {
my ($k,$v) = $kv->($line,$kv_rx);
$map{$k} = [ $spl->($v,$spl_rx) ];
}
print Dumper \%map;
__DATA__
# comment line - ignore
# ignore also empty lines
key1 = value
key2 = value1, value2, value3
It produces the following hash on the provided input:
$VAR1 = {
'key2' => [
'value1',
'value2',
'value3'
],
'key1' => [
'value'
]
};
you might be interested in this SO question as well as this one.
the following code is a self-contained perl script destined to give you an idea of how to implement in perl (only partially in a functional style; in case you don't revulse seeing the particular coding style and/or language construct, i can refine the solution somewhat).
Miguel Prz is right that in most cases you'd search CPAN for solutions to match your requirements.
my (
$is_interesting_line
, $re_is_comment_line
, $re_key_values
, $re_splitter
);
$re_is_comment_line = qr(^\s*#);
$re_key_values = qr(^\s*(\w+)\s*=\s*(.*)$);
$re_splitter = qr(\s*,\s*);
$is_interesting_line = sub {
my $line = shift;
return (
(!(
!defined($line)
|| ($line eq '')
))
&& ($line =~ /$re_key_values/)
);
};
sub strip {
my $line = shift;
# your implementation goes here
return $line;
}
sub parse {
my #lines = #_;
#
my (
$dict
, $interesting_lines
, $k
, $v
);
#
#$interesting_lines =
grep {
&{$is_interesting_line} ( $_ );
} ( map { strip($_); } #lines )
;
$dict = {};
map {
if ($_ =~ /$re_key_values/) {
($k, $v) = ($1, [split(/$re_splitter/, $2)]);
$$dict{$k} = $v;
}
} #$interesting_lines;
return $dict;
} # parse
#
# sample execution goes here
#
my $parse =<<EOL;
# comment
what = is, this, you, wonder
it = is, perl
EOL
parse ( split (/[\r\n]+/, $parse) );

How do you access function parameters in 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.

How can I apply a function to a list using map?

I want to apply a function to every item of a list and store results similar to map(function, list) in python.
Tried to pass a function to map, but got this error:
perl -le 'my $s = sub {}; #r = map $s 0..9'
panic: ck_grep at -e line 1.
What's the proper way to do this?
If a scalar variable holds a code reference -- for example:
my $double = sub { 2 * shift };
You can invoke the code very much the way you would in Python, like this:
$double->(50); # Returns 100.
Applying that to a map example:
my #doubles = map $double->($_), 1..10;
Or this way:
my #doubles = map { $double->($_) } 1..10;
The second variant is more robust because the block defined by the {} braces can contain any number of Perl statements:
my #doubles = map {
my $result = 2 * $_;
# Other computations, if needed.
$result; # The return of each call to the map block.
} 1..10;
my $squared = sub {
my $arg = shift();
return $arg ** 2;
};
then either
my #list = map { &$squared($_) } 0 .. 12;
or
my #list = map { $squared->($_) } 0 .. 12;
or maybe
my $squared;
BEGIN {
*Squared = $squared = sub(_) {
my $arg = shift();
return $arg ** 2;
};
}
my #list = map { Squared } 0 .. 12;
try : map { $s->($_) } (0..9) instead of map $s 0..9
explanation : in you example, $s is a reference to a subroutine, so you must dereference it to allow subroutin calling. This can be achieved in several ways : $s->() or &$s() (and probably some other ways that I'm forgetting)
It's not too different from Python.
#results = map { function($_) } #list;
#results = map function($_), #list;
or with "lambdas",
#results = map { $function->($_) } #list;
#results = map $function->($_), #list;

In Perl, how can I call a method whose name I have in a string?

I'm trying to write some abstract code for searching through a list of similar objects for the first one whose attributes match specific values. In order to do this, I need to call a bunch of accessor methods and check all their values one by one. I'd like to use an abstraction like this:
sub verify_attribute {
my ($object, $attribute_method, $wanted_value) = #_;
if ( call_method($object, $attribute_method) ~~ $wanted_value ) {
return 1;
}
else {
return;
}
}
Then I can loop through a hash whose keys are accessor method names and whose values are the values I'm looking for for those attributes. For example, if that hash is called %wanted, I might use code like this to find the object I want:
my $found_object;
FINDOBJ: foreach my $obj (#list_of_objects) {
foreach my $accessor (keys %wanted) {
next FINDOBJ unless verify_attribute($obj, $accessor, $wanted{$accessor});
}
# All attrs verified
$found_object = $obj;
last FINDOBJ;
}
Of course, the only problem is that call_method does not exsit. Or does it? How can I call a method if I have a string containing its name? Or is there a better solution to this whole problem?
my $found_object;
FINDOBJ: foreach my $obj (#list_of_objects) {
foreach my $accessor (keys %wanted) {
next FINDOBJ unless $obj->$accessor() == $wanted{$accessor};
}
# All attrs verified
$found_object = $obj;
last;
}
Yes, you can call methods this way. No string (or any other) eval involved.
Also, substitute == with eq or =~ depending on the type of the data...
Or, for some extra credits, do it the functional way: (all() should really be part of List::Util!)
use List::Util 'first';
sub all (&#) {
my $code = shift;
$code->($_) || return 0 for #_;
return 1;
}
my $match = first {
my $obj = $_;
all { $obj->$_ == $attrs{$_} }
keys %wanted
} #list_of_objects;
Update: Admittedly, the first solution is the less obfuscated one, so it's preferable. But as somebody answering questions, you have add a little sugar to make it interesting for yourself, too! ;-)
Functional way is cool, but for dummies like me eval rules:
test.pl
#!/usr/bin/perl -l
use F;
my $f = F->new();
my $fun = 'lol'; # method of F
eval '$f->'.$fun.'() '; # call method of F, which name is in $fun var
F.pm
package F;
sub new
{
bless {};
}
sub lol
{
print "LoL";
}
1;
[root#ALT-24 root]# perl test.pl
LoL