Perl: передача двух хэшей в переполнение стека

#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: Я знаю, что мы можем это сделать. Я задавался вопросом, почему вы этого не сделали, учитывая, что это была самая очевидная ошибка в исходном коде.