Perl Closure and References - perl

Hello I'm trying to write a simple subroutine that will compare two numbers to see if one is greater than the other, less than or equal.
So far I have the following code:
sub Value
{ my $num = $_[0];
my ($last) = shift;
my $compare = sub {
if ($last < $last) {print "Less than \n"; } else {print "Greater than \n";};};
my $hashtable;
$hashtable->{"compare"} = $compare;
$hashtable; }
#Execute Statement
my $num1 = Value(57.8);
my $num2 = Value(129.6);
print "Check: ", $num1->{"compare"}->($num2);
Does anyone have suggestion how I can get this to work correctly? Thanks!

You messed up your argument unpacking in Values. You assign the first argument to $num, and then shift the first argument into $last, so $num and $last will always have the same value.
You compare $last with $last, which isn't useful.
You put your closure into $hashtable->{compare}, but execute the contents of the check field, which is undef.
Your closure prints data to the currently selected filehandle, but doesn't return any useful information. Printing the return value doesn't seem sensible.
$num1 and $num2 are closures, and not numbers. Passing an argument to the closure doesn't do anything, as your closure doesn't unpack any arguments.
Here is a implementation that should address your issues:
use strict; use warnings;
use Test::More;
sub create_closure {
my ($x) = #_;
my $operations = {
compare => sub { my ($y) = #_; return $x <=> $y },
add => sub { my ($y) = #_; return $x + $y },
value => $x,
};
return $operations;
}
# some tests
my $ops = create_closure(15);
ok( $ops->{compare}->(15) == 0, "compare to self" );
ok( $ops->{compare}->(20) < 0, "compare to larger");
ok( $ops->{add}->(5) == 20, "add");
ok( $ops->{value} == 15, "value");
my $ops1 = create_closure(150);
ok( $ops1->{compare}->($ops->{value}) > 0, "compare to smaller value");
done_testing;
Edit
You cannot directly compare two $ops, but we can create a field that returns the original value.
However, you might want to use objects and operator overloading if you intend to do such things more often:
use strict; use warnings; use Test::More;
{
package Ops;
sub new {
my ($class, $val) = #_;
if (ref $val eq __PACKAGE__) {
($val, $class) = ($$val, __PACKAGE__);
}
bless \$val => $class;
}
use overload
# overload numeric coercion
'0+' => sub { ${ $_[0] } },
# overload addition. Take care to dereference to avoid infinite loops.
'+' => sub {
my ($self, $other) = #_;
Ops->new($$self + $other);
},
# overload numeric comparision. Take care to swap the args if neccessary.
'<=>' => sub {
my ($self, $other, $swapped) = #_;
(my $val, $other) = $swapped ? ($other, $$self) : ($$self, $other);
Ops->new($val <=> $other);
}
}
my $ops1 = Ops->new( 15);
my $ops2 = Ops->new(150);
# some tests
ok( ($ops1 <=> 15) == 0, "compare to self" );
ok( ($ops1 <=> 20) < 0, "compare to larger");
ok( ($ops1 + (5)) == 20, "add");
ok( $ops1 == 15, "value");
ok( ($ops2 <=> $ops1) > 0, "compare to smaller value");
done_testing;

do it like this:
our $last;
sub compare
{
my ($x, $y) = #_;
if( $x > $y )
{
print("$x is greater than $y\n");
}
elsif( $x == $y )
{
print("$x is equal to $y\n");
}
else
{
print("$x is less than $y\n");
}
$last = ($x, $y);
};
my $lastValues = compare(3, 4); # pass numbers which you want to compare instead of 3 and 4
print("last compared value = $lastValues");

Related

Tail call Recursion "Optimising"

I have a weird problem I can't figure out. I created a simple sequence in Perl with anonymous functions.
sub{($data, sub{($data, sub{($data, sub{($data, empty)})})})};
And it works but I tired to implement tail optimizing and got some weird behaviour. Example. The iter function below works.
sub iter {
my ($func, $seq) = #_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
#_ = ($func, $next);#This #_ update works fine
goto &iter;
}
}
while this implementation of iter fails.
sub iter {
my ($func, $seq) = #_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
$_[1] = $next; #This #_ update fails
goto &iter;
}
}
Both updates of #_ yield the same values for #_ but the code behaves differently when it continues.. To see what I'm talking about try running the complete code below.
#! /usr/bin/env perl
package Seq;
use 5.006;
use strict;
use warnings;
sub empty {
sub{undef};
}
sub add {
my ($data, $seq) = #_;
sub{($data, $seq)};
}
sub iter {
my ($func, $seq) = #_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
#_ = ($func, $next);#This works fine
#$_[1] = $next; #This fails
goto &iter;
}
}
sub smap {
my ($func, $seq) = #_;
my ($data, $next) = $seq->();
if (defined $data) {
sub{($func->($data), Seq::smap($func, $next))};
}else {
empty();
}
}
sub fold {
my ($func, $acc, $seq) = #_;
my ($data, $next) = $seq->();
if (defined $data) {
#_ = ($func, $func->($acc, $data), $next);
goto &Seq::fold;
}else {
$acc;
}
}
1;
package main;
use warnings;
use strict;
use utf8;
use List::Util qw(reduce);
my $seq =
reduce
{Seq::add($b, $a)}
Seq::empty,
(4143, 1234, 4321, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Seq::iter(sub{my ($data) = #_; STDOUT->print("$data\n")}, $seq);
my $seq2 = Seq::smap(sub{my ($data) = #_; $data * 2}, $seq);
STDOUT->print("\n\n");
Seq::iter(sub{my ($data) = #_; STDOUT->print("$data\n")}, $seq2);
STDOUT->print("\n\n");
my $ans = Seq::fold(sub{my ($acc, $data) = #_; $acc + $data}, 0, $seq);
my $ans2 = Seq::fold(sub{my ($acc, $data) = #_; $acc + $data}, 0, $seq2);
STDOUT->print("$ans\n");
STDOUT->print("$ans2\n");
exit (0);
The code should work for both examples of iter but it doesn't.. Any pointers why?
Writing to $_[1] writes to the second scalar passed to the sub.
$ perl -E'$x = "abc"; say $x; sub { $_[0] = "def"; say $_[0]; }->($x); say $x;'
abc
def
def
So you are clobbering the caller's variables. Assigning to #_ replaces the scalars it contains rather than writing to them.
$ perl -E'$x = "abc"; say $x; sub { #_ = "def"; say $_[0]; }->($x); say $x;'
abc
def
abc
You can replace a specific element using splice.
$ perl -E'$x = "abc"; say $x; sub { splice(#_, 0, 1, "def"); say $_[0]; }->($x); say $x;'
abc
def
abc
It's far more convenient for iterators to return an empty list when they are exhausted. For starters, it allows them to return undef.
Furthermore, I'd remove the expensive recursive calls with quicker loops. These loops can be made particularly simple because of the change mentioned above.
The module becomes:
package Seq;
use strict;
use warnings;
sub empty { sub { } }
sub add {
my ($data, $seq) = #_;
return sub { $data, $seq };
}
sub iter {
my ($func, $seq) = #_;
while ( (my $data, $seq) = $seq->() ) {
$func->($data);
}
}
sub smap {
my ($func, $seq) = #_;
if ( (my $data, $seq) = $seq->() ) {
return sub { $func->($data), smap($func, $seq) };
} else {
return sub { };
}
}
sub fold {
my ($func, $acc, $seq) = #_;
while ( (my $data, $seq) = $seq->() ) {
$acc = $func->($acc, $data);
}
return $acc;
}
1;
Also, for speed reasons, replace
sub { my ($data) = #_; $data * 2 }
sub { my ($acc, $data) = #_; $acc + $data }
with
sub { $_[0] * 2 }
sub { $_[0] + $_[1] }

perl: getting a value from a function of the object

So I have a class AClass with variables (x, y), and a function which should take two objects as arguments of the same class, compute their x and y, and return a new instance of the class with computed values.
package AClass;
sub new {
my $class = shift;
my $x = shift;
my $y = shift;
my $self = {
x => $x,
y => $y
};
return bless($self, $class);
}
sub getX {
my $self = shift;
return $self->{'x'};
}
sub getY {
my $self = shift;
return $self->{'y'};
}
sub addition {
my ($c1, $c2) = #_;
return new AClass(
$c1->getX() + $c1->getX(),
$c1->getY() + $c2->getY()
);
}
my $a1 = AClass->new(6, 4);
my $a2 = AClass->new(4, 3);
my $val = AClass::addition(\$v1, \$v2);
say $val.getX();
I'm getting error "Can't call method "getX" on unblessed reference". I think the problem is in addition function, when I'm trying to access the values of the objects which are not the real numbers or ?
There is a number of problems here.
You are using $v1 and $v2 when presumably you mean $a1 and $a2
You are passing references to those objects, instead of the objects themselves
Your addition method adds the X value of $c1 to itself instead of to the X value of $c2
You are using the string concatenation operator . instead of the indirection operator ->
It is best to use lower-case letters for lexical identifiers. Capitals are generally reserved for globals like package names
You must always use strict and use warnings at the top of your program. In this case you would have been alerted to the fact that $v1 and $v2 hadn't been declared.
This version of your code works fine
use strict;
use warnings;
package AClass;
sub new {
my $class = shift;
my ($x, $y) = #_;
bless { x => $x, y => $y }, $class;
}
sub get_x {
my $self = shift;
$self->{x};
}
sub get_y {
my $self = shift;
$self->{y};
}
sub addition {
my ($c1, $c2) = #_;
AClass->new(
$c1->get_x + $c2->get_x,
$c1->get_y + $c2->get_y
);
}
package main;
use feature 'say';
my $a1 = AClass->new(6, 4);
my $a2 = AClass->new(4, 3);
my $val = AClass::addition($a1, $a2);
say $val->get_x;
output
10
You use $v1 instead $a1. Always use use strict; use warnings;.
Also, you're taking a reference for no reason.
my $val = AClass::addition($a1, $a2);
The following would also work (though add) would be a better word:
my $val = $a1->addition($a2);

Unless constructor argument passed is a hash type, croak on invalid arguments?

I am vaguely confused a bit on different methods of passing certain arguments to the constructor type. I want to only pass a hash reference \%hash, or a list foo => 1, bar => 1 but not both and croak if anything else is passed i.e ( single elements, array reference ).
For example, I pass my reference or list.. (This works for the way I do this)
my $obj = foo->new;
my $data = $obj->dump( \%hash );
my $data = $obj->dump( foo => 1, bar => 1 );
or
my $obj = foo->dump( \%hash );
my $obj = foo->dump( foo => 1, bar => 1 );
Package module:
package foo;
use strict;
use Carp;
use Scalar::Util qw/reftype/;
sub new { return bless {}, shift }
sub dump {
my $class = shift;
my $self = shift;
unless ( reftype( $self ) eq reftype {} ) {
croak("Constructor method not a hash type!");
}
}
1;
I've also thought about using the conditional operator ? : here, but I can't get it to error properly.
my $self = reftype($_[0]) eq reftype {} ? shift : {#_};
Is there a better preferred way to do this?
We can look at the various ways your dump method can be called.
If we pass a "hash list", the number of elements is even (#_ % 2 == 0). Also, if at least one key-value pair is present, the first argument (a key) is a string, so not defined reftype $_[0] holds.
If we pass a hash reference, then the argument list should only hold this reference, and no other values: #_ == 1. The first argument will be a hash: reftype($_[0]) eq 'HASH'.
So to put the arguments in a hash reference, one could do something like:
sub dump {
my $invocant = shift;
my $hashref;
if (#_ == 1 and reftype $_[0] eq 'HASH') {
$hashref = $_[0];
} elsif (#_ % 2 == 0 and (#_ == 0 or not defined reftype $_[0])) {
$hashref = +{ #_ };
} else {
croak "Unknown argument format: either pass a hashref, or an even-valued list";
}
...; # do something with $hashref
}
To find out if the $invocant is the class name or an object, just ask it if it is blessed:
if (defined Scalar::Util::blessed $invocant) {
say "Yep, it is an object";
} else {
say "Nope, it is a package name";
}
There's no such thing as a "hash list". foo => 1, bar => 1, is just a four element list. Sounds like you want to accept hash refs and even numbers of args.
sub dump {
my $self = shift;
my %args;
if (#_ == 1) {
croak("...") if (ref($_[0]) // '') ne 'HASH';
%args = %{ $_[0] };
} else {
croak("...") if #_ % 2 != 0;
%args = #_;
}
...
}

Only one of two arguments should be defined

What is the more elegant way to write the next?
sub depend {
my($x,$y) = #_;
die "only one allowed" if( defined($x) && defined($y) );
die "one must be defined" unless ( defined($x) || defined($y) );
if( defined($x) ) {
$y = somefunc($x);
} else {
$x = somefunc($y);
}
return($x,$y);
}
The function should get exactly only one argument. If get defined both = error, if defined none = error. And the undefined arg is calculated based on the defined one.
Use xor, i.e. the "exclusive or":
sub depend {
my ($x, $y) = #_;
die "Exactly one must be defined.\n" unless defined $x xor defined $y;
if (defined $x) {
$y = somefunc($x);
} else {
$x = somefunc($y);
}
return($x, $y);
}
Update: You can shorten the rest of the sub, too. Instead of the if part, just put
return ($x // somefunc($y), $y // somefunc($x));
I might define the subroutine to take two arguments, but treat them as a key-value pair. To use the width/height example from your comment:
sub depend {
my $key = shift;
my $value = shift;
die "One parameter only" if #_;
return ($value, calc_height($value)) if $key eq "width";
return (calc_width($value), $value) if $key eq "height";
die "Must specify either height or width, not $key";
}
my ($w1, $h1) = depend( width => 5 );
my ($w2, $h2) = depend( height => 10 );
my ($w3, $h3) = depend(); # ERROR Must specify either height or width
my ($w4, $h4) = depend( other=>3 ); # ERROR Must specify either height or width, not other
my ($w5, $h5) = depend( foo => bar, 7); # ERROR, One parameter only
Try this:
sub f
{
my ($x, $y) = #_;
die "BOOM" if (defined $x ? 1 : 0) +
(defined $y ? 1 : 0) != 1;
}
Using xor might not be intuitive, and solution below could easily be extended to more input arguments,
sub depend {
my($x,$y) = #_;
die "There should be only one!" if (grep defined, $x,$y) != 1;
if( defined($x) ) {
$y = somefunc($x);
}
else {
$x = somefunc($y);
}
return($x,$y);
}

How can I flatten the arguments to my subroutine into an array?

Consider following script:
use strict;
use Data::Dumper;
my #arr=('1A','2A');
my $arr_ref=['1','2'];
sub routine1
{
my #arr=#_;
print Dumper(\#arr);
}
routine1(#arr,'one_A');
sub routine2
{
my $arr_ref=[#_];
print Dumper($arr_ref);
}
routine2($arr_ref,'one');
routine1 is using #arr and routine2 is using $arr_ref.
routine1 prints the following:
$VAR1 = [
'1A',
'2A',
'one_A'
];
routine2 prints following:
$VAR1 = [
[
'1',
'2'
],
'one'
];
I want to continue using #_ and arr_ref in routine2 but want to come up with below output:
$VAR1 = [
'1',
'2'
'one'
];
Can someone suggest the way out?
Using the function ref you can see if a scalar is a reference (and if so, which type). In a simplistic case where only array references will be passed you can simply use this to flatten the inputs.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
sub test {
my #arr = map { ref() ? #$_ : $_ } #_;
print Dumper \#arr;
}
test( ['a', 'b'], 1 );
As a side benefit, this code will die with a message if a reference to another type is passed, since you attempt to deference as an array. If you need to handle more, you will need to check the reference type. This starts to build in complexity quickly.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
sub test {
my #arr = map {
my $type = ref;
if ( ! $type ) {
$_;
} elsif ( $type eq 'ARRAY' ) {
#$_;
} elsif ( $type eq 'HASH' ) {
%$_;
} else {
()
}
} #_;
print Dumper \#arr;
}
test( ['a', 'b'], { p => 'q' }, 1 );
By returning an empty list for other reference types I silently ignore all other reference types. Or perhaps you would rather force stringification on other reference types.
...
} else {
"$_";
}
...
test( ['a','b'], sub{}, bless({},'MyClass'), 1 );
Of couse which of these handlings to use depends on you use case.
Just wrote this the other day at work.
sub flatten {
return map { ref($_) ? flatten(#{$_}) : ($_) } #_;
}
This program shows a subroutine flatten that will flatten a mixed list of simple data and array references, nested to any level.
use strict;
use warnings;
use Data::Dump;
my #arr = qw/ 1A 2A /;
my $arr_ref = [1, 2];
sub flatten;
routine1(#arr, 'one_A');
routine2($arr_ref, 'one');
sub routine1 {
my #arr=#_;
dd \#arr;
}
sub routine2 {
my $arr_ref = [flatten #_];
dd $arr_ref;
}
sub flatten {
my $i = 0;
while ($i < #_) {
my $item = $_[$i];
if (ref $item eq 'ARRAY') {
splice #_, $i, 1, #$item;
}
else {
++$i;
}
}
#_;
}
output
["1A", "2A", "one_A"]
[1, 2, "one"]