Qual é a maneira mais segura para percorrer as chaves de um hash de Perl?

votos
84

Se eu tiver um hash Perl com um monte de pares (chave, valor), o que é o método preferido de iteração através de todas as chaves? Ouvi dizer que o uso de eachMaio de alguma forma têm efeitos colaterais não intencionais. Então, isso é verdade, e é um dos dois métodos a seguir melhores, ou se existe uma maneira melhor?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Publicado 06/08/2008 em 03:53
fonte usuário
Em outras línguas...                            


9 respostas

votos
170

A regra de ouro é usar a função mais adequada às suas necessidades.

Se você quiser apenas as chaves e não planejar sempre ler qualquer um dos valores, use as teclas ():

foreach my $key (keys %hash) { ... }

Se você quiser apenas os valores, use valores ():

foreach my $val (values %hash) { ... }

Se você precisar as chaves e os valores, use each ():

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

Se você pretende mudar as chaves do hash de qualquer forma , exceto para apagar a chave atual durante a iteração, então você não deve usar cada (). Por exemplo, este código para criar um novo conjunto de chaves em maiúsculas com os valores dobraram funciona bem usando as teclas ():

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

produzindo o hash resultante esperado:

(a => 1, A => 2, b => 2, B => 4)

Mas usando cada () para fazer a mesma coisa:

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

produz resultados incorretos em formas difíceis de prever. Por exemplo:

(a => 1, A => 2, b => 2, B => 8)

Isso, no entanto, é seguro:

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

Tudo isso é descrito na documentação perl:

% perldoc -f keys
% perldoc -f each
Respondeu 06/08/2008 em 14:22
fonte usuário

votos
21

Uma coisa que você deve estar ciente de quando se usa eaché que ele tem o efeito colateral de adicionar "estado" para o seu haxixe (o hash tem que lembrar o que a tecla "próximo" é). Ao usar um código como os trechos postados acima, que iterar sobre todo o hash de uma só vez, isso geralmente não é um problema. No entanto, você vai correr em difícil de rastrear problemas (Falo por experiência;), quando se utiliza eachem conjunto com declarações como lastou returna saída do while ... eachcircuito antes de ter processado todas as chaves.

Neste caso, o hash vai se lembrar que as chaves já voltou, e quando você usa eachnele na próxima vez (talvez em um pedaço totalmente independentes de código), ele continuará nesta posição.

Exemplo:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

Isto imprime:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

O que aconteceu com as chaves "bar" e baz "? Eles ainda estão lá, mas o segundo eachcomeça onde o primeiro parou, e pára quando chega ao fim do hash, então nós nunca vê-los no segundo loop.

Respondeu 16/09/2008 em 00:37
fonte usuário

votos
19

O lugar onde eachvocê pode causar problemas é que é um verdadeiro iterador, não escopo. A título de exemplo:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

Se você precisa ter certeza de que eachrecebe todas as chaves e valores, você precisa ter certeza de que você usa keysou valuesprimeira (como que redefine o iterador). Veja a documentação para cada .

Respondeu 16/09/2008 em 15:35
fonte usuário

votos
13

Usando a cada sintaxe irá evitar todo o conjunto de teclas sejam gerados ao mesmo tempo. Isso pode ser importante se você estiver usando um hash tie-ed para um banco de dados com milhões de linhas. Você não quer para gerar toda a lista de chaves de uma só vez e esgotar sua memória física. Neste caso, cada serve como uma iteração Considerando teclas gera, na verdade, toda a matriz antes de o circuito começa.

Assim, o único lugar "cada" é de uso real é quando o hash é muito grande (em comparação com a memória disponível). Isso só é provável de acontecer quando o próprio hash não viver com a memória em si a menos que você está programando um dispositivo de recolha de dados portátil ou algo com memória pequeno.

Se a memória não é um problema, geralmente o mapa ou as teclas paradigma é o mais prevalente e mais fácil de ler paradigma.

Respondeu 11/09/2008 em 23:04
fonte usuário

votos
5

Alguns pensamentos diversos sobre este tema:

  1. Não há nada inseguro sobre qualquer um dos próprios iteradores hash. O que não é seguro modificar as chaves de um hash, enquanto você está interagindo sobre ele. (É perfeitamente seguro para modificar os valores.) O único efeito colateral potencial que eu posso pensar é que valuesretorna aliases que significa que modificá-los irá modificar o conteúdo do hash. Isso ocorre por design, mas pode não ser o que você quer em algumas circunstâncias.
  2. De John resposta aceita é bom com uma exceção: a documentação é claro que não é seguro para adicionar chaves, enquanto a iteração sobre um hash. Ele pode funcionar para alguns conjuntos de dados, mas falhará para outros, dependendo da ordem hash.
  3. Como já foi referido, é seguro excluir a última chave retornado por each. Isto é não verdadeiro para keyscomo eaché um iterador, enquanto keysretorna uma lista.
Respondeu 15/09/2008 em 22:36
fonte usuário

votos
4

Eu sempre usar o método 2 também. A única vantagem de usar cada um é se você está apenas lendo (em vez de re-atribuição) o valor da entrada de hash, você não está constantemente de-referenciando o hash.

Respondeu 06/08/2008 em 05:04
fonte usuário

votos
4

I pode ser mordido por um presente, mas eu acho que é preferência pessoal. Não consigo encontrar qualquer referência na documentação para cada () ser diferente do que as teclas () ou valores () (exceto o óbvio "eles retornam coisas diferentes" resposta. Na verdade, os docs indicar o uso da mesma iteração e todos eles retornar valores de lista reais em vez de cópias dos mesmos, e que modificar o hash enquanto Iterando sobre ele usando qualquer chamada é ruim.

Tudo o que disse, eu quase sempre usar teclas (), porque para mim é normalmente mais auto documentação para acessar o valor da chave através do próprio hash. I ocasionalmente usar valores () quando o valor é uma referência a uma grande estrutura e a chave para o hash já foi armazenado na estrutura, no ponto em que a chave é redundante e não precisa. Eu acho que eu usei cada () 2 vezes em 10 anos de programação Perl e foi provavelmente a escolha errada ambas as vezes =)

Respondeu 06/08/2008 em 04:43
fonte usuário

votos
1

Eu costumo usar keyse eu não posso pensar na última vez que eu usei ou ler um uso de each.

Não se esqueça map, dependendo do que você está fazendo no circuito!

map { print "$_ => $hash{$_}\n" } keys %hash;
Respondeu 22/08/2008 em 16:31
fonte usuário

votos
-2

I woudl dizer:

  1. Use o que é mais fácil de ler / entender para a maioria das pessoas (de modo chaves, normalmente, eu diria)
  2. Use o que você decidir de forma consistente durante todo toda a base de código.

Este dar 2 principais vantagens:

  1. É mais fácil de detectar código "comum" para que você possa re-factor em funções / methiods.
  2. É mais fácil para desenvolvedores de futuros de manter.

Eu não acho que é mais caro para usar chaves sobre cada, por isso não há necessidade de duas construções diferentes para a mesma coisa em seu código.

Respondeu 20/12/2010 em 13:46
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more