[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Estou vendo esse código, mas meu cérebro não está registrando como o número 10 pode se tornar o resultado. Alguém se importaria de explicar o que está acontecendo aqui?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Estou vendo esse código, mas meu cérebro não está registrando como o número 10 pode se tornar o resultado. Alguém se importaria de explicar o que está acontecendo aqui?
Respostas:
Você pode pensar no primeiro argumento do bloco como um acumulador: o resultado de cada execução do bloco é armazenado no acumulador e depois passado para a próxima execução do bloco. No caso do código mostrado acima, você está padronizando o acumulador, resultado, para 0. Cada execução do bloco adiciona o número fornecido ao total atual e, em seguida, armazena o resultado novamente no acumulador. A próxima chamada em bloco tem esse novo valor, adiciona-o, armazena-o novamente e repete-o.
No final do processo, injetar retorna o acumulador, que neste caso é a soma de todos os valores na matriz ou 10.
Aqui está outro exemplo simples para criar um hash a partir de uma matriz de objetos, codificada por sua representação de string:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
Nesse caso, estamos padronizando nosso acumulador para um hash vazio e preenchendo-o sempre que o bloco é executado. Observe que devemos retornar o hash como a última linha do bloco, porque o resultado do bloco será armazenado novamente no acumulador.
result + explanation
é tanto a transformação no acumulador quanto o valor de retorno. É a última linha do bloco, tornando-o um retorno implícito.
inject
pega um valor para começar ( 0
no exemplo) e um bloco e executa esse bloco uma vez para cada elemento da lista.
result + element
).A maneira mais fácil de explicar isso pode ser mostrar como cada etapa funciona, por exemplo; este é um conjunto imaginário de etapas, mostrando como esse resultado pode ser avaliado:
[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10
A sintaxe para o método injetar é a seguinte:
inject (value_initial) { |result_memo, object| block }
Vamos resolver o exemplo acima, ou seja
[1, 2, 3, 4].inject(0) { |result, element| result + element }
que dá o 10 como a saída.
Portanto, antes de começar, vamos ver quais são os valores armazenados em cada variável:
resultado = 0 O zero veio da injeção (valor) que é 0
elemento = 1 É o primeiro elemento da matriz.
Okey !!! Então, vamos começar a entender o exemplo acima
Passo 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Passo 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Etapa 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Passo 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Etapa: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Aqui , os valores em negrito-itálico são elementos buscados na matriz e os valores em negrito são os valores resultantes.
Espero que você entenda o funcionamento do #inject
método da #ruby
.
O código itera sobre os quatro elementos na matriz e adiciona o resultado anterior ao elemento atual:
O que eles disseram, mas observe também que você nem sempre precisa fornecer um "valor inicial":
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
é o mesmo que
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Experimente, eu espero.
Quando nenhum argumento é passado para injetar, os dois primeiros elementos são passados para a primeira iteração. No exemplo acima, o resultado é 1 e o elemento é 2 na primeira vez, portanto, menos uma chamada é feita para o bloco.
O número que você coloca dentro do seu injetor () representa um ponto de partida, pode ser 0 ou 1000. Dentro dos tubos, você tem dois marcadores de posição | x, y |. x = qualquer número que você tenha dentro do .inject ('x'), e o segundo representa cada iteração do seu objeto.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Injetar aplica o bloco
result + element
para cada item na matriz. Para o próximo item ("elemento"), o valor retornado do bloco é "resultado". Da maneira que você chamou (com um parâmetro), "resultado" começa com o valor desse parâmetro. Portanto, o efeito é somar os elementos.
tldr; inject
difere de map
uma maneira importante: inject
retorna o valor da última execução do bloco, enquanto map
retorna o array que iterou.
Mais do que isso, o valor de cada execução do bloco passou para a próxima execução pelo primeiro parâmetro ( result
neste caso) e você pode inicializar esse valor (a (0)
parte).
Seu exemplo acima pode ser escrito usando o map
seguinte:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
O mesmo efeito, mas inject
é mais conciso aqui.
Você encontrará frequentemente uma atribuição no map
bloco, enquanto uma avaliação ocorre noinject
bloco.
O método escolhido depende do escopo desejado result
. Quando não usar, seria algo como isto:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
Você pode ser como tudo: "Olha só, eu acabei de combinar tudo isso em uma linha", mas você também alocou temporariamente a memória x
como uma variável temporária que não era necessária, pois você já tinha result
que trabalhar.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
é equivalente ao seguinte:
def my_function(r, e)
r+e
end
a = [1, 2, 3, 4]
result = 0
a.each do |value|
result = my_function(result, value)
end
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Em inglês simples, você está passando (iterando) por essa matriz ( [1,2,3,4]
). Você irá percorrer essa matriz 4 vezes, porque existem 4 elementos (1, 2, 3 e 4). O método injetar possui 1 argumento (o número 0) e você adicionará esse argumento ao 1º elemento (0 + 1. Isso é igual a 1). 1 é salvo no "resultado". Em seguida, adicione esse resultado (que é 1) ao próximo elemento (1 + 2. Isso é 3). Este vai agora ser salvo como o resultado. Continue: 3 + 3 é igual a 6. E, finalmente, 6 + 4 é igual a 10.
Esse código não permite a possibilidade de não passar um valor inicial, mas pode ajudar a explicar o que está acontecendo.
def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end
incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10
Comece aqui e revise todos os métodos que aceitam blocos. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
É o bloco que confunde você ou por que você tem um valor no método? Boa pergunta embora. Qual é o método do operador lá?
result.+
Como é que começa?
#inject(0)
Nós podemos fazer isso?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
Isto funciona?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
Você vê que estou desenvolvendo a ideia de que simplesmente soma todos os elementos da matriz e gera um número no memorando que você vê nos documentos.
Você sempre pode fazer isso
[1, 2, 3, 4].each { |element| p element }
para ver o enumerável da matriz ser iterado. Essa é a ideia básica.
É apenas que injetar ou reduzir fornecem um memorando ou um acumulador que é enviado.
Poderíamos tentar obter um resultado
[1, 2, 3, 4].each { |result = 0, element| result + element }
mas nada volta, então isso funciona da mesma maneira que antes
[1, 2, 3, 4].each { |result = 0, element| p result + element }
no bloco inspetor de elementos.
Esta é uma explicação simples e bastante fácil de entender:
Esqueça o "valor inicial", pois é um pouco confuso no começo.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
Você pode entender o que foi dito acima: Estou injetando uma "máquina de adicionar" entre 1,2,3,4. Ou seja, é 1 ♫ 2 ♫ 3 ♫ 4 e ♫ é uma máquina de somar, portanto é igual a 1 + 2 + 3 + 4 e é 10.
Você pode realmente injetar um +
entre eles:
> [1,2,3,4].inject(:+)
=> 10
e é como, injetar um +
entre 1,2,3,4, tornando-o 1 + 2 + 3 + 4 e é 10. :+
É a maneira de Ruby especificar+
na forma de um símbolo.
Isso é muito fácil de entender e intuitivo. E se você deseja analisar como funciona passo a passo, é como: pegar 1 e 2, e agora adicioná-los, e quando você tiver um resultado, armazene-o primeiro (que é 3) e agora, a seguir é o armazenado o valor 3 e o elemento da matriz 3 passando pelo processo a + b, que é 6, e agora armazena esse valor, e agora 6 e 4 passam pelo processo a + b, e é 10. Você está fazendo essencialmente
((1 + 2) + 3) + 4
e é 10. O "valor inicial" 0
é apenas uma "base" para começar. Em muitos casos, você não precisa disso. Imagine se você precisar de 1 * 2 * 3 * 4 e é
[1,2,3,4].inject(:*)
=> 24
e está feito. Você não precisa de um "valor inicial" 1
para multiplicar a coisa toda 1
.
Existe outra forma de método .inject () Isso é muito útil [4,5] .inject (&: +) Isso adicionará todo o elemento da área
É o mesmo que isto:
[1,2,3,4].inject(:+)
=> 10