Existem benefícios em usar essa variável extra na anotação do loop for?

11

Encontrei a seguinte anotação de loop em um grande projeto no qual estou trabalhando (pseudocódigo):

var someOtherArray = [];
for (var i = 0, n = array.length; i < n; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

O que chamou minha atenção é essa variável "n" extra. Eu nunca vi um for lop escrito dessa maneira antes.

Obviamente, nesse cenário, não há razão para que esse código não possa ser escrito da seguinte maneira (com a qual estou muito acostumado):

var someOtherArray = [];
for (var i = 0; i < array.length; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

Mas isso me fez pensar.

Existe um cenário em que escrever um loop for faria sentido? A ideia vem à mente de que o comprimento do "array" pode mudar durante a execução do loop for, mas não queremos um loop além do tamanho original, mas não consigo imaginar esse cenário.

Encolher a matriz dentro do loop também não faz muito sentido, porque provavelmente obteremos OutOfBoundsException.

Existe um padrão de design conhecido em que essa anotação é útil?

Editar Conforme observado por @ Jerry101, o motivo é o desempenho. Aqui está um link para o teste de desempenho que eu criei: http://jsperf.com/ninforloop . Na minha opinião, a diferença não é grande o suficiente, a menos que você esteja repetindo uma variedade muito grande. O código do qual copiei tinha apenas 20 elementos, então acho que a legibilidade neste caso supera a consideração de desempenho.

Vlad Spreys
fonte
Em reação a você editar: isso é programação orientada a objetos. O comprimento padrão da matriz é rápido e barato. Mas, por qualquer motivo, a implementação do .length pode mudar para algo caro. Se um valor permanecer o mesmo, não o obtenha várias vezes.
Pieter B
Concordo. É bom saber sobre essa opção, mas provavelmente não a usaria com muita frequência, a menos que obtenha um exemplo semelhante à sua consulta sql. Obrigado you☺
Vlad Spreys
O problema é que, se sua matriz tiver um milhão de itens, você chamará array.length um milhão de vezes em vez de uma vez, enquanto o valor permanecer o mesmo. Pedir algo um milhão de vezes e saber que cada resposta subsequente será a mesma que a primeira resposta ...... me parece um pouco absurdo.
Pieter B
1
Não acho que isso torne o código significativamente mais difícil de ler. (Sério, se alguém tiver problemas com um loop tão simples, deve ficar preocupado.) Mas: 1) Eu ficaria terrivelmente surpreso se, após 4 décadas de linguagens C e C, todos os compiladores sérios ainda não fizeram isso. para voce; e 2) esse pode não ser um hot spot. Não parece valer a pena fazer com que alguém gaste mais 5 segundos analisando-o, a menos que esteja dentro de uma biblioteca (por exemplo, a implementação de uma estrutura de dados).
Doval
1
Em C # / .NET, a variante utilizada npode ser mais lenta que a variante utilizada, array.Lengthpois o JITter pode não perceber que poderia eliminar as verificações dos limites da matriz.
CodesInChaos

Respostas:

27

A variável ngarante que o código gerado não busque o comprimento da matriz para cada iteração.

É uma otimização que pode fazer a diferença no tempo de execução, dependendo do idioma usado, se a matriz é realmente um objeto de coleção ou uma "matriz" JavaScript e outros detalhes de otimização.

Jerry101
fonte
3
Não, não faz. Se o comprimento da matriz é buscado cada iteração depende não só da língua, mas também no compilador e opções do compilador e o que é feito no circuito (estou falando de C e C ++)
BЈовић
.. E, mesmo assim, eu pessoalmente colocaria a atribuição n logo acima do loop, se eu realmente a quisesse, pois - como OP observou - essa é uma construção rara (YMMV) a ser usada e é facilmente perdida / não entendida por outras leituras de desenvolvedores o código.
Cwap
20
Como está escrito, ele abrange no loop, o que normalmente é uma coisa boa. Eu o definiria antes do loop apenas se quisesse usá-lo novamente mais tarde.
precisa saber é o seguinte
4
@ Jerry101: Aparentemente, o exemplo de trecho é JavaScript, onde não existe um escopo de loop ...
Bergi
1
@ Nota: na prática, o compilador raramente consegue provar que o tamanho da matriz não varia (normalmente é trivialmente possível apenas se o vetor sobre o qual você está fazendo loop for local para a função e você estiver chamando apenas funções embutidas no loop).
Matteo Italia
2

A busca do comprimento da matriz pode ser facilmente "mais cara" do que a ação real sobre a qual você itera.

Portanto, se o conjunto não mudar, consulte o comprimento apenas uma vez.

Não com matrizes, mas com conjuntos de registros provenientes de um servidor sql, vi melhorias drásticas ao não consultar a contagem de registros a cada iteração. (é claro, faça isso apenas se você puder garantir que a matriz ou o conjunto de registros não seja alterado durante esse processo).

Pieter B
fonte
Se a contagem de registros mudar, então o que? Digamos que houvesse 100 itens e, enquanto você processa o item 50, um item após o nº 25 é inserido. Portanto, quando você processa o item 51, é o mesmo que você já processou e as coisas dão errado. Portanto, o problema não é que o comprimento muda, é muito mais difundido.
precisa saber é o seguinte
@ gnasher729 Quando você entra em comportamento assimétrico, muita coisa pode dar errado. Mas existem coisas que você pode fazer contra isso, como a transação inicial e o sql.
Pieter B
2

A ideia vem à mente de que o comprimento do "array" pode mudar durante a execução do loop for, mas não queremos um loop além do tamanho original, mas não consigo imaginar esse cenário.

As pessoas que falam sobre desempenho provavelmente estão corretas, por isso foi escrito dessa maneira (a pessoa que escreveu dessa maneira pode não estar correta, pois afeta significativamente o desempenho, mas isso é outra questão).

No entanto, para responder a essa parte da sua pergunta, acho que é mais provável que isso aconteça quando o principal objetivo do loop for modificar o array que ele está repetindo. Considere algo parecido com isto, no pseudo-código:

guests = [];
for (i = 0; i < invitations.length; ++i) {
    if (! invitations[i].is_declined()) {
        guests.append(invitations[i].recipient())
    }
}
// maybe some other stuff to modify the guestlist
for (i = 0, n = guests.length; i < n; ++i) {
    if (guests[i].has_plus_one()) {
        guests.append(guests[i].get_plus_one());
    }
}

Claro que existem outras maneiras de escrevê-lo. Nesse caso, eu poderia ter verificado outros mais ao mesmo tempo como se os destinatários aceitavam seus convites e produzido a lista completa de convidados, um convite por vez. Não quero necessariamente combinar essas duas operações, mas não consigo pensar imediatamente em uma razão pela qual isso esteja definitivamente errado e, portanto, esse design é necessário . É uma opção a considerar, ocasionalmente.

Na verdade, acho que a coisa mais errada nesse código é que a associação à guestsmatriz significa coisas diferentes em pontos diferentes do código. Portanto, eu certamente não chamaria isso de "padrão", mas não sei se há defeito suficiente para descartar a possibilidade de fazer algo desse tipo :-)

Steve Jessop
fonte
A matriz também pode ser modificada por outro thread. Isso impede que o compilador otimize a recuperação de comprimento. Então, em questão, o programador diz explicitamente que o array não muda mesmo em outros threads.
Basilevs
0

Se possível, você deve usar um iterador que atravessa todos os elementos da matriz. Você usa a matriz apenas como uma sequência, não como armazenamento de acesso aleatório; portanto, seria óbvio que um iterador que apenas faz um acesso seqüencial seria mais rápido. Imagine o que você acha que uma matriz é na verdade uma árvore binária, mas alguém escreveu um código que determina o "comprimento" contando os elementos e o código que acessa o i-ésimo elemento, também percorrendo o elemento, cada um em O (n ) Um iterador pode ser muito rápido, seu loop será sloooooow.

gnasher729
fonte