Reputation: 279
Below is a small sub to calculate distance between two points by passing two hashes each containing the x and y coordinates. I get a "syntax error near ]{" fatal error at the line calling the sub. Just started on Perl yesterday, not entirely sure what I am doing. How do I pass two hashes to a sub to return a value? Tried this without much success and not sure what I need to work on (hope it is ok to refer to an outside link).
%dot1 = ('x'=>5, 'y'=>6);
%dot2 = ('x'=>7, 'y'=>8);
sub dist {
my (%hash1) = @_[0];
my (%hash2) = @_[1];
$dist = ((@_[0]{'x'}-@_[1]{'x'})**2 + (@_[0]{'y'}-@_[1]{'y'})**2)**0.5;
}
$D = dist(\%dot1,\%dot2);
Upvotes: 1
Views: 1810
Reputation: 85767
First and foremost, you should start every file with
use strict;
use warnings;
This lets Perl catch the most obvious errors in code.
This part is mostly fine, but under use strict
Perl will complain about %dot1
and %dot2
being undeclared (and without strict
they will be implicitly global, which is usually not what you want):
%dot1 = ('x'=>5, 'y'=>6);
%dot2 = ('x'=>7, 'y'=>8);
Change it to
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
The call
$D = dist(\%dot1,\%dot2);
has the same problem: It should be
my $D = dist(\%dot1,\%dot2);
What it does is pass references to %dot1
and %dot2
to the sub dist
.
my (%hash1) = @_[0];
This line doesn't make much sense: @_[0]
is a list slice, returning a list of elements of @_
corresponding to the indices 0
. In other words, it's a one-element slice and better written as $_[0]
, accessing the single element directly.
But in either case it doesn't make sense to assign a single element to a hash. Perl will interpret it as a key and set the corresponding value to undef
. Your call passed \%dot1
as the first argument, so $_[0]
is a reference to a hash. By using it as a hash key, Perl will convert it to a string, yielding something like "HASH(0x0075ADD40)"
.
Your choices at this point are to either dereference the reference right there and make a copy:
my %hash1 = %{ $_[0] }; # effectively performs %hash1 = %dot1
Or keep the reference and dereference it each time you need access to the hash:
my $hashref1 = $_[0]; # $hashref1->{foo} accesses $dot1{foo} directly
$dist = ((@_[0]{'x'}-@_[1]{'x'})**2 + (@_[0]{'y'}-@_[1]{'y'})**2)**0.5;
There are a few issues here. First, you don't need the (implicitly global) variable $dist
. You just want to return a value from the sub, which can be done with return
. Then, as explained above, @_[0]
and @_[1]
should be $_[0]
and $_[1]
, respectively. Fixing that we get
return (($_[0]{'x'} - $_[1]{'x'}) ** 2 + ($_[0]{'y'} - $_[1]{'y'}) ** 2) ** 0.5;
This does indeed work ($_[0]{'x'}
is syntactic sugar for $_[0]->{'x'}
, i.e. this expression dereferences the hash reference stored in $_[0]
to reach the 'x'
key of %dot1
).
But we didn't use the variables we just created at all. Depending on which way you want to go, you should replace $_[0]{foo}
by either $hash1{foo}
or $hashref1->{foo}
(and similar for $_[1]
and %hash2
/$hashref2
).
Finally, instead of ** 0.5
we can just use sqrt
.
Here's how I'd write it:
use strict;
use warnings;
sub dist {
my ($p1, $p2) = @_;
return sqrt(($p1->{x} - $p2->{x}) ** 2 + ($p1->{y} - $p2->{y}) ** 2);
}
my %dot1 = (x => 5, y => 6);
my %dot2 = (x => 7, y => 8);
my $D = dist(\%dot1, \%dot2);
print "Result: $D\n";
Upvotes: 5
Reputation: 69244
Many problems here, I'm afraid.
First you, need to add use strict
and use warnings
to your code. This will point out many errors. Mainly places where you use @array[index]
but should have used $array[index]
. You also don't declare any of your variables.
Fixing all of that gives me this code:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
sub dist {
my (%hash1) = $_[0];
my (%hash2) = $_[1];
my $dist = (($_[0]{'x'}-$_[1]{'x'})**2 + ($_[0]{'y'}-$_[1]{'y'})**2)**0.5;
}
my $D = dist(\%dot1,\%dot2);
say $D;
This still doesn't work. I get:
$ perl twohash
Reference found where even-sized list expected at twohash line 11.
Reference found where even-sized list expected at twohash line 12.
2.82842712474619
The errors are where you are assigning the two arguments you pass into dist()
.
my (%hash1) = $_[0];
my (%hash2) = $_[1];
You are passing references to hashes, not the hashes themselves (And you're right to do that), but this means that you get scalars, not hashes, in the subroutine. So those lines need to be:
my ($hash1) = $_[0];
my ($hash2) = $_[1];
Making those changes, the code now works and gives the result "2.82842712474619".
I'll just point out one further strangeness in your code - you assign the parameters to the function to two lexical variables ($hash1
and $hash2
) but you then ignore those variables and instead go directly to @_
for this data. I expect you actually want:
my $dist = (($hash1->{'x'}-$hash2->{'x'})**2 + ($hash1->{'y'}-$hash2->{'y'})**2)**0.5;
Note, I've changed $_[0]{'x'}
to $hash1->{'x'}
as you have a reference to a hash.
All in all, this code is a bit of a mess and I suggest you go back to the very earliest chapters of whatever book you are learning Perl from.
Upvotes: 2
Reputation: 2589
Could you please try this:
use Data::Dumper;
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
sub dist {
my %hash1 = @_[0];
my %hash2 = @_[1];
$dist = ((@_[0]->{'x'}-@_[1]->{'x'})**2 + (@_[0]->{'y'}-@_[1]->{'y'})**2)**0.5;
}
$D = dist(\%dot1,\%dot2);
print $D;
Upvotes: -1