Perl: отправка хэшей и общих переменных - PullRequest
3 голосов
/ 15 ноября 2011

У меня есть модуль с набором функций, реализованных в виде хеш-функции диспетчеризации с вспомогательной функцией, таким образом:

my $functions = {
  'f1' => sub { 
      my %args = @_;
      ## process data ...
      return $answer; 
  },
[etc.]
};

sub do_function {
    my $fn = shift;
    return $functions->{$fn}(@_);
}

Это используется некоторыми сценариями, которые обрабатывают данные, разделенные табуляцией;проверяемый столбец конвертируется соответствующей подпрограммой.При обработке значения в столбце я передаю хэш данных подпрограмме, и он генерирует скаляр, новое значение для столбца.

В настоящее время подпрограммы называются следующим образом:

my $new_value = do_function( 'f1', data => $data, errs => $errs );

и все переменные в аргументах объявлены как 'my' - мои $ data, мои $ errs и т. Д. Возможно ли обновить другие значения в аргументах, передаваемых в подпрограммы, без необходимости их возврата?т.е. вместо того, чтобы делать это:

 ... in $functions->{f1}:
      my %args = @_;
      ## process data ...
      ## alter $args{errs}
      $args{errs}->{type_one_error}++; 
      ## ...
      return { answer => $answer, errs => $args{errs} }; 
 ...

 ## call the function, get the response, update the errs
 my $return_data = do_function( 'f1', data => $data, errs => $errs );
 my $new_value = $return_data->{answer};
 $errs = $return_data->{errs}; ## this has been altered by sub 'f1'

Я мог бы сделать это:

  my $new_value = do_function( 'f1', data => $data, errs => $errs );
  ## no need to update $errs, it has been magically updated already!

Ответы [ 2 ]

3 голосов
/ 15 ноября 2011

Вы можете передать ссылку на значение и обновить ее внутри подпрограммы.

Например:

sub update {
    my ($ref_to_value) = @_;
    $$ref_to_value = "New message";
    return "Ok";
}

my $message = "Old message";

my $retval = update(\$message);

print "Return value: '$retval'\nMessage: '$message'\n";

И, насколько я вижу из фрагментов кода, $errsуже ссылка на хеш.Так что, на самом деле, все, что вам нужно сделать - просто закомментировать строку $errs = $return_data->{errs}; и попробовать

Если я правильно понял ваш код, $errs будет обновлено.И тогда вы должны просто изменить возвращаемое значение на $answer и сделать:

my $new_value = do_function( 'f1', data => $data, errs => $errs );
1 голос
/ 16 ноября 2011
  • сначала измените определение do_function на:

    sub do_function {
        my $fn = shift;
        goto &{$functions->{$fn}}
    }
    

    , что является правильным способом отправки в новую подпрограмму.эта форма goto заменяет текущую выполняемую подпрограмму новым coderef, передавая @_ без изменений и удаляя do_function из стека вызовов (поэтому caller работает правильно).вы, вероятно, также хотите проверить наличие ошибок, чтобы убедиться, что $fn является допустимым именем.

  • внутри вашей функции, вы можете просто изменить ячейки @_ напрямую ивам не нужно ничего передавать по ссылке (поскольку Perl уже сделал это для вас).

    sub add1 {$_[0]++}
    my $x = 1;
    add1 $x;
    say $x; # 2
    

    для поддержки key => value аргументов без передачи по ссылке, вы можете написать это следующим образом:

    in $functions->{f1}:
      my %args;
      while (@_) {
          $args{$_} = /errs/ ? \shift : shift for shift
      }
      ## process data ...
      ## alter ${$args{errs}}
      ## ...
    
  • HOWEVER , поскольку в вашем случае $errs является ссылкой на хеш, вам не нужно выполнять никакой дополнительной работы.все ссылки передаются по ссылке автоматически.в вашем существующем коде все, что вам нужно сделать, это изменить ключ $args{errs} (как вы делаете сейчас), и он изменит каждую ссылку на этот хеш.

    , если вы хотите использовать локальный хэш функции,вам нужно сделать копию хэша *:

    my %errs = %{$args{errs}};
    

    , где %errs является приватным, и как только вы закончите, вы можете вставить любые значения, которые хотите сделать общедоступными, в $args{errs} с помощью $args{errs}{...} = ...;.но не заменяйте $args{errs} на копию (как в $args{errs} = \%errs), поскольку это нарушит соединение с хэшем ошибки вызывающей стороны.если вы хотите скопировать все новые значения в, вы можете использовать одно из:

    %{$args{errs}} = %errs;                             # replace all keys
    @{$args{errs}}{keys %errs} = values %errs;          # replace keys in %errs
    ... and $args{errs}{$_} = $errs{$_} for keys %errs; # conditional replace
    

    * или локализовать некоторые / все ключи

...