2012-09-21 2 views
1

Так что, пытаясь исправить ошибку в последнее время, парень-хакер сказал мне, что поскольку значение строки, переданной подпрограмме (методу), может быть довольно большой, доступ к ней через $_[1] позволит избежать копирования памяти. Однако, я думал, что любое значение, переданное подпрограмме, было скопировано в @_? поэтому в приведенном ниже примере память копируется дважды? или я ошибаюсь в отношении копии, сделанной при передаче методу?Имеет ли доступ к @_ непосредственно к копии памяти большого скаляра?

sub foo { 
    my $self = shift 

    $_[0] # access $str in @_ directly 
    my ($str) = @_; # makes another copy of @_ 
} 

sub bar { 
    my $self = shift; 
    my $str = 'something very large'; 

    $self->foo($str); #copies $str to the @_ of foo 
} 

Вот почему я предложил автору, позволяющей пройти мимо скалярной реф, который позволит избежать копии (кроме самой ссылки) при переходе к самому методу. Повторить: передает ли значение подпрограмме среднее значение копируется в @_?

+0

Одна вещь, которую следует помнить: «Преждевременная оптимизация - это корень всего зла». –

+1

@eugeney спасибо, но понимание того, как все работает, не – xenoterracide

+2

@eugeney Итак, мы можем создать специальный путь в 'sqrt (evil)', который немедленно возвращает «Преждевременная оптимизация», правильно? –

ответ

7

Согласно http://perldoc.perl.org/perlsub.html (курсив мой):

Любые аргументы, переданные в Обнаруживаются в массиве @_. Поэтому, если вы вызываете функцию с двумя аргументами, они будут храниться в $_[0] и $_[1]. Массив @_ - это локальный массив, но его элементы являются псевдонимами для реальных скалярных параметров. В частности, если обновлен элемент $_[0], соответствующий аргумент обновляется (или возникает ошибка, если он не является обновляемым). Если аргумент представляет собой массив или хэш-элемент, который не существовал при вызове функции, этот элемент создается только тогда, когда (и если) он модифицирован или ссылка на него выполняется. (Некоторые более ранние версии Perl создали элемент независимо от того, назначен ли ему элемент.) Присвоение целому массиву @_ удаляет это псевдонимы и не обновляет никаких аргументов.

По моему показанию, это указывает на то, что по умолчанию в @_ копия не копируется.

Хотя я буду признавать, что используемый язык немного тупой.

2

Нет копии для $_[0], которая не возникла бы для $str, так как это два разных имени для одной и той же переменной.

$ perl -E'my $str; sub { say \$str == \$_[0] ?1:0 }->($str);' 
1 

Это назначение, которое делает копию в my ($str) = @_;.

5

Да, элементы в @_ с псевдонимом. Никакая копия не приводит к передаче аргументов в подпрограммы.

Это означает, что вы можете сделать полезные, но удивительные вещи, как:

sub strip { 
    $_[0] =~ s{^\s+}{}; 
    $_[0] =~ s{\s+$}{}; 
} 

my $var = " foo "; 
strip($var); 
print $var; # "foo" 

Такое действие на расстоянии, как правило, удивительно и опасно. Пользователю нет никаких указаний на то, что strip изменит свои аргументы. Более безопасная и более очевидная вещь - передать значение в качестве ссылки.

sub strip { 
    my $ref = shift; 
    $$ref =~ s{^\s+}{}; 
    $$ref =~ s{\s+$}{}; 
} 

my $var = " foo "; 
strip(\$var); 
print $var; # "foo" 

Это и экономит память (только ссылка копируется), позволяет назвать аргумент внутри подпрограммы, и так как они должны передать ссылку он позволяет вызывающей знать их переменная может быть изменена.

Альтернативой является использование псевдонима только для чтения.Это дает вам оптимизацию памяти, не копируя переменную, позволяет вам называть переменную, но не позволяет вам случайно ее изменить.

Существует несколько способов достичь этого, но Method::Signatures делает его удобным.

use Method::Signatures; 

func no_copy($string is alias is ro) { 
    # $string is an alias to $var 
    print "$string\n"; 

    # But it cannot be altered because $string is read-only. 
    # This will throw an error. 
    $string .= "bar"; 
} 

my $var = "foo"; 
no_copy($var); 
Смежные вопросы