#perl #hash #parameter-passing
#perl #хэш #передача параметров
Вопрос:
Ниже приведен небольшой подраздел для вычисления расстояния между двумя точками путем передачи двух хэшей, каждый из которых содержит координаты x и y. Я получаю «синтаксическую ошибку около ] {» неустранимую ошибку в строке, вызывающей вспомогательный модуль. Только вчера начал работать с Perl, не совсем уверен, что я делаю. Как мне передать два хэша в sub, чтобы вернуть значение? Пробовал это без особого успеха и не уверен, над чем мне нужно работать (надеюсь, можно ссылаться на внешнюю ссылку).
%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);
Ответ №1:
Прежде всего, вы должны начинать каждый файл с
use strict;
use warnings;
Это позволяет Perl улавливать наиболее очевидные ошибки в коде.
Эта часть в основном в порядке, но в use strict
Perl будет жаловаться %dot1
%dot2
на то, что она не объявлена (и без strict
них они будут неявно глобальными, что обычно не то, что вы хотите):
%dot1 = ('x'=>5, 'y'=>6);
%dot2 = ('x'=>7, 'y'=>8);
Измените его на
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
Вызов
$D = dist(%dot1,%dot2);
имеет ту же проблему: она должна быть
my $D = dist(%dot1,%dot2);
Что он делает, так это передает ссылки на %dot1
и %dot2
на sub dist
.
my (%hash1) = @_[0];
Эта строка не имеет особого смысла: @_[0]
это фрагмент списка, возвращающий список элементов @_
, соответствующих индексам 0
. Другими словами, это одноэлементный фрагмент, и его лучше записать как $_[0]
, напрямую обращаясь к одному элементу.
Но в любом случае не имеет смысла назначать хэшу один элемент. Perl интерпретирует его как ключ и устанавливает соответствующее значение undef
. Ваш вызов, переданный %dot1
в качестве первого аргумента, so $_[0]
является ссылкой на хэш. Используя его в качестве хэш-ключа, Perl преобразует его в строку, выдавая что-то вроде "HASH(0x0075ADD40)"
.
На данный момент вы можете либо разыменовать ссылку прямо там, либо сделать копию:
my %hash1 = %{ $_[0] }; # effectively performs %hash1 = %dot1
Или сохраняйте ссылку и разыменовывайте ее каждый раз, когда вам нужен доступ к хэшу:
my $hashref1 = $_[0]; # $hashref1->{foo} accesses $dot1{foo} directly
$dist = ((@_[0]{'x'}-@_[1]{'x'})**2 (@_[0]{'y'}-@_[1]{'y'})**2)**0.5;
Здесь есть несколько проблем. Во-первых, вам не нужна (неявно глобальная) переменная $dist
. Вы просто хотите вернуть значение из вложенного, что можно сделать с return
помощью . Затем, как объяснено выше, @_[0]
и @_[1]
должно быть $_[0]
и $_[1]
, соответственно. Исправляя это, мы получаем
return (($_[0]{'x'} - $_[1]{'x'}) ** 2 ($_[0]{'y'} - $_[1]{'y'}) ** 2) ** 0.5;
Это действительно работает ( $_[0]{'x'}
является синтаксическим сахаром для $_[0]->{'x'}
, т. Е. Это выражение разыменовывает хэш-ссылку, хранящуюся в $_[0]
, чтобы получить 'x'
ключ %dot1
).
Но мы вообще не использовали только что созданные переменные. В зависимости от того, в какую сторону вы хотите пойти, вы должны заменить $_[0]{foo}
либо $hash1{foo}
или $hashref1->{foo}
(и аналогично для $_[1]
и %hash2
/ $hashref2
).
Наконец, вместо ** 0.5
мы можем просто использовать sqrt
.
Вот как я бы это написал:
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: $Dn";
Комментарии:
1. Это действительно полезно @melpomene. Спасибо, что взяли на себя труд так хорошо это объяснить.
2. @user110084 См . p3rl.org/REF для получения дополнительной информации о ссылках.
Ответ №2:
Боюсь, здесь много проблем.
Сначала вам нужно добавить use strict
и use warnings
в свой код. Это укажет на множество ошибок. В основном места, где вы используете @array[index]
, но должны были использовать $array[index]
. Вы также не объявляете ни одну из своих переменных.
Исправление всего этого дает мне этот код:
#!/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;
Это все еще не работает. Я получаю:
$ 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
Ошибки возникают там, где вы присваиваете два аргумента, в которые вы передаете dist()
.
my (%hash1) = $_[0];
my (%hash2) = $_[1];
Вы передаете ссылки на хэши, а не на сами хэши (и вы правы в этом), но это означает, что вы получаете скаляры, а не хэши, в подпрограмме. Итак, эти строки должны быть:
my ($hash1) = $_[0];
my ($hash2) = $_[1];
После внесения этих изменений код теперь работает и выдает результат «2.82842712474619».
Я просто укажу на еще одну странность в вашем коде — вы присваиваете параметры функции двум лексическим переменным ( $hash1
и $hash2
), но затем игнорируете эти переменные и вместо этого переходите непосредственно @_
к этим данным. Я ожидаю, что вы действительно хотите:
my $dist = (($hash1->{'x'}-$hash2->{'x'})**2 ($hash1->{'y'}-$hash2->{'y'})**2)**0.5;
Обратите внимание, я изменил $_[0]{'x'}
$hash1->{'x'}
, поскольку у вас есть ссылка на хэш.
В целом, этот код немного запутан, и я предлагаю вам вернуться к самым ранним главам любой книги, из которой вы изучаете Perl.
Ответ №3:
Не могли бы вы попробовать это:
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;
Комментарии:
1. Почему вы не исправили все
@_[0]
ошибки$_[0]
?2. Почему вы добавили
Data::Dumper
?3. @DaveCross: Конечно, мы можем это
@_[0]
сделать$_[0]
.4. @melpomene: просто я хочу подтвердить
hash
значенияDumper
.5. @ssr1012: Я знаю, что мы можем это сделать. Я задавался вопросом, почему вы этого не сделали, учитывая, что это была самая очевидная ошибка в исходном коде.