2009-02-13 6 views
14

У меня часто есть подпрограмма в Perl, которая заполняет массив некоторой информацией. Поскольку я также используется для взлома в C++, я считаю себя часто делают это, как это в Perl, используя ссылки:Возвращает целый массив из подпрограммы Perl неэффективно?

my @array; 
getInfo(\@array); 

sub getInfo { 
    my ($arrayRef) = @_; 
    push @$arrayRef, "obama"; 
    # ... 
} 

вместо более простой версии:

my @array = getInfo(); 

sub getInfo { 
    my @array; 
    push @array, "obama"; 
    # ... 
    return @array; 
} 

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

Это правильно? Или Perl оптимизирует это в любом случае?

+0

Возможно, вы могли бы объяснить на более высоком уровне то, что вы пытаетесь сделать. Может быть более современный, Perlish способ писать то, что вы хотите, чтобы избежать вашей проблемы в целом. Или, по крайней мере, сделать ваши намерения более ясными. –

+0

@unknown (google): Я часто это делаю в разных контекстах. Последнее время было, когда я хотел прочитать файл в массиве. Таким образом, файл должен быть открыт, я печатаю информацию о том, какой файл я открываю на STDERR, немного модифицирую строки и вставляю их в массив, а затем закройте файл. – Frank

+0

изменить как? как это можно сделать в каждой строке, с указанием карты? я нахожу толкать что-то на массив, чтобы быть очень подозрительным, снова в пользу других методов. иногда это не может помочь, хотя –

ответ

18

Как насчет возврата ссылки на массив в первую очередь?

sub getInfo { 
    my $array_ref = []; 
    push @$array_ref, 'foo'; 
    # ... 
    return $array_ref; 
} 

my $a_ref = getInfo(); 
# or if you want the array expanded 
my @array = @{getInfo()}; 

Редактировать в соответствии с комментарием dehmann в:

Также можно использовать обычный массив в функции и возвращает ссылку на него.

sub getInfo { 
    my @array; 
    push @array, 'foo'; 
    # ... 
    return \@array; 
}  
+0

Это звучит как лучшее решение для меня! – Powerlord

+0

Собственно, как насчет создания реального массива в функции, но если он возвращает ссылку на него? Perl сохранит локально созданный массив и вернет ссылку эффективно. – Frank

+0

@dehmann: хорошая точка, я включил ваш комментарий в свой ответ, спасибо. – user55400

-4

Я ничего не знаю о Perl, так что это нейтральный для языка ответ.

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

Эффективное решение для подпрограммы передать вызывающей программе адрес массива. Как я уже сказал, я не имею в виду поведение по умолчанию в Perl. Но некоторые языки предоставляют программисту возможность выбрать, какой подход.

+0

«Адрес массива» в Perl является ссылкой. Вопрос в том, оптимизирует ли Perl для него. –

13

Передача ссылок более эффективна, но разница не такая большая, как в C++. Значения самих аргументов (это означает: значения в массиве) всегда передаются по ссылке в любом случае (возвращенные значения копируются хотя).

Вопрос: это имеет значение? В большинстве случаев это не так. Если вы возвращаете 5 элементов, не беспокойтесь об этом. Если вы возвращаете/пропускаете 100 000 элементов, используйте ссылки. Только оптимизируйте его, если это узкое место.

3

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

Если вы имеете дело с большими объемами данных или если производительность является серьезной проблемой, то ваши привычки C послужат вам хорошо передают и возвращают ссылки на структуры данных, а не сами структуры, чтобы они не нуждались для копирования. Но, как отметил Леон Тиммерманс, подавляющее большинство времени, вы имеете дело с меньшими объемами данных и производительности, не так уж и важны, поэтому сделать это каким-либо образом кажется наиболее читаемым.

8

Если я смотрю на ваш пример, и думать о том, что вы хотите сделать, я привык писать таким образом:

sub getInfo { 
    my @array; 
    push @array, 'obama'; 
    # ... 
    return \@array; 
} 

Мне кажется, как простой версии, когда мне нужно вернуть большой количество данных.Нет необходимости в выделить массив вне sub, как вы писали в своем первом фрагменте кода, потому что my сделайте это за вас. В любом случае вам не следует делать преждевременную оптимизацию, как Leon Timmermanssuggest.

2

Так я обычно возвращаю массив.

sub getInfo { 
    my @array; 
    push @array, 'foo'; 
    # ... 
    return @array if wantarray; 
    return \@array; 
} 

Таким образом, он будет работать так, как вы хотите, в скалярном или контекстном списке.

my $array = getInfo; 
my @array = getInfo; 

$array->[0] == $array[0]; 

# same length 
@$array == @array; 

Я бы не стал оптимизировать его, если вы не знаете, что это медленная часть вашего кода. Даже тогда я бы использовал контрольные показатели, чтобы увидеть, какая подпрограмма на самом деле быстрее.

+0

Тогда вы не можете получить счет, присвоив getInfo() скалярному значению. У http://perlmonks.org/?node_id=729965 есть интересные дебаты об использовании wantarray. – daotoad

+0

Я согласен, я использовал для использования 'wantarray' около трех лет назад. Я был жестким, это классная особенность. После многолетнего опыта работы в большом проекте perl со многими разными квалифицированными разработчиками я принял решение, что код контекстной информации является одним из самых худших в Perl. –

+1

@daotoad: вы никогда не можете предположить, что функция, возвращающая список в контексте списка, вернет свою длину в скалярном контексте, поскольку это происходит только тогда, когда функция возвращает массив. Если функция возвращает значение списка, вы получите последний элемент списка. Зачем? Потому что Perl HATES вы. :) –

2

Существует два соображения. Очевидным является то, насколько большой будет ваш массив? Если это меньше нескольких десятков элементов, тогда размер не является фактором (если вы не оптимизируете микро-функцию для некоторой быстро называемой функции, но вам придется сделать некоторое профилирование памяти, чтобы доказать это в первую очередь).

Это легкая часть. Часто игнорируется второе соображение - интерфейс. Как будет использоваться возвращаемый массив? Это важно, потому что разыменование целых массивов в Perl выглядит ужасно. Например:

for my $info (@{ getInfo($some, $args) }) { 
    ... 
} 

Это уродливое. Это намного лучше.

for my $info (getInfo($some, $args)) { 
    ... 
} 

Он также поддается картированию и grepping.

my @info = grep { ... } getInfo($some, $args); 

Но возвращающий реф массив может быть удобно, если вы собираетесь выбрать отдельные элементы:

my $address = getInfo($some, $args)->[2]; 

Это проще, чем:

my $address = (getInfo($some, $args))[2]; 

Или:

my @info = getInfo($some, $args); 
my $address = $info[2]; 

Но в этот момент вы шо uld вопрос, является ли @info действительно списком или хешем.

my $address = getInfo($some, $args)->{address}; 

То, что вы не должны сделать, это getInfo() возвращают реф массив в скалярном контексте и массив в контексте списка. Это мешает традиционному использованию скалярного контекста как длины массива, что удивит пользователя.

Наконец, я подключу свой собственный модуль, Method::Signatures, потому что он предлагает компромисс для передачи ссылок на массивы без использования синтаксиса ref ref.

use Method::Signatures; 

method foo(\@args) { 
    print "@args";  # @args is not a copy 
    push @args, 42; # this alters the caller array 
} 

my @nums = (1,2,3); 
Class->foo(\@nums); # prints 1 2 3 
print "@nums";  # prints 1 2 3 42 

Это делается с помощью магии Data::Alias.

+0

Вы никогда не можете предположить, что функция, возвращающая список в контексте списка, вернет свою длину в скалярном контексте, поскольку это происходит только тогда, когда функция возвращает массив.Если функция возвращает значение списка без массива, вы получите последний элемент списка вместо его размера. –

+0

Тогда не возвращайте списки! Если ваша рукоятка молотка дает вам осколки, не надевайте перчатки, не плавайте! Вся вещь «list vs array» в Perl 5 - гигантская, зияющая медвежья ловушка прямо в середине игровой площадки. – Schwern

+0

Я полностью согласен с вашим последним предложением. Я бы добавил, что большинство детей на детской площадке и, возможно, даже дизайнеры игровых площадок, не знают об этой медвежьей ловушке. :) –

0

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

  1. Выключайте буферизацию sysread() вместо чтения() (руководство предупреждает о смешивание)
  2. Предварительно расширить массив, ценя последний элемент - сохраняет распределение памяти
  3. Используйте Распаковка(), чтобы быстро разделить данные, такие как данные uint16_t графика канала

Передача массива ref в функцию позволяет основной программе иметь дело с простым массивом, в то время как рабочая функция write-once-and-forget использует более сложный доступ «$ @» и arrow -> [$ II] формы. Будучи вполне C'ish, он, скорее всего, будет быстрым!

Смежные вопросы