A regra é usar a função mais adequada às suas necessidades.
Se você deseja apenas as chaves e não planeja ler nenhum dos valores, use keys ():
foreach my $key (keys %hash) { ... }
Se você quiser apenas os valores, use values ():
foreach my $val (values %hash) { ... }
Se você precisar das chaves e dos 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ê planeja alterar as chaves do hash de qualquer forma, exceto para excluir a chave atual durante a iteração, você não deve usar each (). Por exemplo, este código para criar um novo conjunto de chaves maiúsculas com valores duplicados funciona bem usando keys ():
%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 each () 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 de maneiras 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 do perl:
% perldoc -f keys
% perldoc -f each
Uma coisa que você deve estar ciente ao usar
each
é que ela tem o efeito colateral de adicionar "estado" ao seu hash (o hash precisa lembrar qual é a "próxima" chave). Ao usar o código como os snippets postados acima, que iteram todo o hash de uma vez, isso geralmente não é um problema. No entanto, você encontrará problemas difíceis de rastrear (falo por experiência própria;), ao usareach
junto com instruções comolast
oureturn
para sair dowhile ... each
loop antes de processar todas as chaves.Nesse caso, o hash lembrará quais chaves já retornou e, quando você usá
each
-lo da próxima vez (talvez em um código totalmente não relacionado), ele continuará nesta posição.Exemplo:
Isso imprime:
O que aconteceu com as chaves "bar" e baz "? Elas ainda estão lá, mas a segunda
each
começa onde a primeira parou e para quando chega ao final do hash, então nunca as vemos no segundo loop.fonte
O que
each
pode causar problemas é que ele é um iterador verdadeiro, sem escopo. A título de exemplo:Se você precisa ter certeza de que
each
obtém todas as chaves e valores, precisa ter certeza de usarkeys
ouvalues
primeiro (pois isso redefine o iterador). Veja a documentação de cada um .fonte
Usar a sintaxe each evitará que todo o conjunto de chaves seja gerado de uma vez. Isso pode ser importante se você estiver usando um hash vinculado a um banco de dados com milhões de linhas. Você não deseja gerar toda a lista de chaves de uma vez e esgotar sua memória física. Nesse caso, cada um serve como um iterador, enquanto as chaves, na verdade, geram todo o array antes do início do loop.
Portanto, o único lugar em que "cada" tem uso real é quando o hash é muito grande (em comparação com a memória disponível). É provável que isso aconteça apenas quando o próprio hash não viver na memória, a menos que você esteja programando um dispositivo portátil de coleta de dados ou algo com pouca memória.
Se a memória não for um problema, geralmente o paradigma do mapa ou das chaves é o paradigma mais predominante e mais fácil de ler.
fonte
Algumas idéias diversas sobre este tópico:
values
retorna aliases, o que significa que modificá-los irá modificar o conteúdo do hash. Isso ocorre por design, mas pode não ser o que você deseja em algumas circunstâncias.each
. Isso não é verdade parakeys
aseach
é um iterador enquantokeys
retorna uma lista.fonte
Eu sempre uso o método 2 também. O único benefício de usar cada um é se você estiver apenas lendo (em vez de reatribuir) o valor da entrada de hash, você não está constantemente desreferenciando o hash.
fonte
Posso ser mordido por este, mas acho que é uma preferência pessoal. Não consigo encontrar nenhuma referência nos documentos para cada () ser diferente de keys () ou valores () (além da resposta óbvia "eles retornam coisas diferentes". Na verdade, os documentos declaram que se use o mesmo iterador e todos eles retorna valores de lista reais em vez de cópias deles, e que modificar o hash enquanto iterando sobre ele usando qualquer chamada é ruim.
Dito isso, quase sempre uso keys () porque, para mim, geralmente é mais autodocumentado acessar o valor da chave por meio do próprio hash. Ocasionalmente, uso values () quando o valor é uma referência a uma grande estrutura e a chave para o hash já estava armazenada na estrutura, ponto em que a chave é redundante e eu não preciso dela. Acho que usei each () 2 vezes em 10 anos de programação Perl e provavelmente foi a escolha errada nas duas vezes =)
fonte
Eu costumo usar
keys
e não consigo pensar na última vez que usei ou li um uso deeach
.Não se esqueça
map
, dependendo do que você está fazendo no loop!fonte
Eu diria:
Isso dá duas vantagens principais:
Não acho que seja mais caro usar chaves em vez de cada uma, então não há necessidade de duas construções diferentes para a mesma coisa em seu código.
fonte
keys
o uso de memória aumenta emhash-size * avg-key-size
. Dado que o tamanho da chave é limitado apenas pela memória (já que são apenas elementos de array como "seus" valores correspondentes sob o capô), em algumas situações pode ser proibitivamente mais caro no uso de memória e no tempo gasto para fazer a cópia.