Pergunta

Muitas vezes eu tenho uma sub-rotina em Perl que preenche uma matriz com algumas informações. Desde que eu também estou acostumado a cortar em C ++, encontro-me muitas vezes fazê-lo como este em Perl, usando referências:

my @array;
getInfo(\@array);

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

em vez da versão mais simples:

my @array = getInfo();

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

O motivo, claro, é que eu não quero que a matriz a ser criado localmente na sub-rotina e, em seguida, copiado no retorno.

Não é certo? Ou será que Perl otimizar isso fora de qualquer maneira?

Foi útil?

Solução

O que sobre o retorno uma referência de matriz em primeiro lugar?

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()};

Edite de acordo com o comentário de dehmann:

Também é possível usar uma matriz normal na função e retornar uma referência a ele.

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

Outras dicas

Passando referências é mais eficiente, mas a diferença não é tão grande como em C ++. Os valores dos argumentos próprios (o que significa: os valores na matriz) são sempre passados ??por referência de qualquer maneira (valores devolvidos são copiados embora)

.

A pergunta é: que importa? Na maioria das vezes, isso não acontece. Se você está retornando 5 elementos, não se preocupar com isso. Se você está retornando / passando 100'000 elementos, referências de uso. Apenas otimizá-lo se é um gargalo.

Se eu olhar para o seu exemplo e pensar sobre o que você quer fazer Eu estou acostumado a escrevê-lo desta maneira:

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

Parece-me como Versão simples quando eu preciso retornar grande quantidade de dados. Não há necessidade de alocar array fora sub como você escrito em seu primeiro trecho de código porque my fazer isso por você. De qualquer forma você não deve fazer otimização prematura como Leon Timmermans sugerem .

Para responder à ruminação final, não, Perl não otimizar essa distância. Não se pode, realmente, porque retornando um array e retornar um escalar são fundamentalmente diferentes.

Se você está lidando com grandes quantidades de dados ou se o desempenho é uma grande preocupação, então seus hábitos C irá atendê-lo bem - passagem e retorno referências a estruturas de dados, em vez das próprias estruturas de modo que eles não precisam ser copiado. Mas, como Leon Timmermans apontou, a grande maioria das vezes, você está lidando com menores quantidades de dados e desempenho não é esse negócio um grande, assim fazê-lo de qualquer maneira parece mais legível.

Esta é a maneira que eu normalmente retornar um array.

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

Desta forma, ele vai funcionar da maneira que quiser, em escalar ou lista contextos.

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

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

# same length
@$array == @array;

Eu não iria tentar otimizá-lo, a menos que você sabe que é uma parte lenta do seu código. Mesmo assim, eu iria usar benchmarks para ver qual sub-rotina é realmente mais rápido.

Há duas considerações. A mais óbvia é o quão grande é a sua gama vai ficar? Se for menos de algumas dezenas de elementos, em seguida, o tamanho não é um fator (a menos que você está micro-otimização para alguma função rapidamente chamado, mas você teria que fazer alguma memória de perfil para provar que em primeiro lugar).

Essa é a parte fácil. A segunda consideração oft esquecido é a interface. Como é a matriz retornada vai ser utilizado? Isto é importante porque toda gama dereferencing é meio terrível em Perl. Por exemplo:

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

Isso é feio. Isto é muito melhor.

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

Ele também presta-se ao mapeamento e Descobrir.

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

Mas retornando um array ref pode ser útil se você estiver indo para escolher os elementos individuais:

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

Isso é mais simples do que:

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

Ou:

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

Mas nesse ponto, você deve questionar se @Info é verdadeiramente uma lista ou um hash.

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

O que você não deve fazer é ter getInfo() retornar um array ref no contexto escalar e uma matriz no contexto de lista. Este confusões o uso tradicional de contexto escalar como comprimento da matriz que vai surpreender o usuário.

Finalmente, vou ligar o meu próprio módulo, Método :: Signatures , porque oferece um compromisso para passar em referências de matriz sem ter que usar a sintaxe de matriz 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

Isto é feito através da magia da Data :: Alias ??.

3 outras melhorias de desempenho potencialmente grande se você está lendo todo um, arquivo largish e cortando-o em um array:

  1. Desligue BUFFERING com sysread () em vez de read () (manual de alerta sobre a mistura)
  2. Pré-estender a matriz valorizando o último elemento - salva as alocações de memória
  3. Use Descompacte () para rapidamente dados de divisão como dados uint16_t gráficos do canal

Passando um ref matriz para a função permite que o programa principal para lidar com uma matriz simples, enquanto o write once-e esquecer a função do trabalhador usa o mais complicado "$ @" e flecha -> [$ II] formas de acesso. Sendo bastante C'ish, é provável que seja rápido!

Não sei nada sobre Perl por isso esta é uma resposta de linguagem neutra.

É, de certa forma, ineficiente para copiar um array de uma sub-rotina para o programa de chamada. A ineficiência surge na memória extra utilizado eo tempo necessário para copiar os dados de um lugar para outro. Por outro lado, para todos, mas os maiores matrizes, que você pode não dou a mínima, e pode preferir copiar matrizes para fora para elegância, obstinação ou qualquer outro motivo.

A solução eficiente é para a sub-rotina de passar o programa de chamada o endereço do array. Como eu disse, eu não tenho a menor idéia sobre o comportamento padrão do Perl a este respeito. Mas algumas linguagens fornecem o programador a opção de escolher qual abordagem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top