Precisa de uma explicação simples do método de injeção
142
[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?
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.
ótima explicação, no entanto, no exemplo dado pelo OP, o que está sendo retornado (como o hash está no seu exemplo). Termina com resultado + explicação e deve ter um valor de retorno, sim?
precisa saber é o seguinte
1
@Projjol result + explanationé tanto a transformação no acumulador quanto o valor de retorno. É a última linha do bloco, tornando-o um retorno implícito.
KA01 15/09/16
87
injectpega um valor para começar ( 0no exemplo) e um bloco e executa esse bloco uma vez para cada elemento da lista.
Na primeira iteração, ele passa o valor que você forneceu como valor inicial e o primeiro elemento da lista e salva o valor que seu bloco retornou (neste caso result + element).
Em seguida, ele executa o bloco novamente, passando o resultado da primeira iteração como o primeiro argumento e o segundo elemento da lista como o segundo argumento, salvando novamente o resultado.
Ele continua assim até consumir todos os elementos da lista.
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)+410
Obrigado por escrever as etapas. Isso ajudou muito. Embora eu tenha ficado um pouco confuso sobre se você quer dizer que o diagrama abaixo é como o método injetar é implementado abaixo em termos do que é passado como argumento para injetar.
2
O diagrama abaixo é baseado em como ele pode ser implementado; não é necessariamente implementado exatamente dessa maneira. Por isso eu disse que é um conjunto imaginário de etapas; demonstra a estrutura básica, mas não a implementação exata.
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
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; injectdifere de mapuma maneira importante: injectretorna o valor da última execução do bloco, enquanto mapretorna 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 ( resultneste caso) e você pode inicializar esse valor (a (0)parte).
Seu exemplo acima pode ser escrito usando o mapseguinte:
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 mapbloco, 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 xcomo uma variável temporária que não era necessária, pois você já tinha resultque trabalhar.
[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
É 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 }
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" 1para multiplicar a coisa toda 1.
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:
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.
fonte
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:
fonte
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
.fonte
O código itera sobre os quatro elementos na matriz e adiciona o resultado anterior ao elemento atual:
fonte
O que eles disseram, mas observe também que você nem sempre precisa fornecer um "valor inicial":
é o mesmo que
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.
fonte
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
fonte
Injetar aplica o bloco
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.
fonte
tldr;
inject
difere demap
uma maneira importante:inject
retorna o valor da última execução do bloco, enquantomap
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: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: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á tinharesult
que trabalhar.fonte
é equivalente ao seguinte:
fonte
[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.fonte
Esse código não permite a possibilidade de não passar um valor inicial, mas pode ajudar a explicar o que está acontecendo.
fonte
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á?
Como é que começa?
Nós podemos fazer isso?
Isto funciona?
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
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
mas nada volta, então isso funciona da mesma maneira que antes
no bloco inspetor de elementos.
fonte
Esta é uma explicação simples e bastante fácil de entender:
Esqueça o "valor inicial", pois é um pouco confuso no começo.
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: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
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 ée está feito. Você não precisa de um "valor inicial"
1
para multiplicar a coisa toda1
.fonte
Existe outra forma de método .inject () Isso é muito útil [4,5] .inject (&: +) Isso adicionará todo o elemento da área
fonte
É apenas
reduce
oufold
, se você estiver familiarizado com outros idiomas.fonte
É o mesmo que isto:
fonte