주입 방법에 대한 간단한 설명이 필요합니다


142
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

나는이 코드를보고 있지만 뇌는 숫자 10이 어떻게 결과가 될 수 있는지 등록하지 않습니다. 여기서 무슨 일이 일어나고 있는지 설명해 주시겠습니까?

ruby  syntax 

3
Wikipedia : Fold (고차 함수)를 참조하십시오 : inject는 "왼쪽 접힘"이지만, 불행히도 루비 사용에 부작용이 있습니다.
user2864740

답변:


208

첫 번째 블록 인수를 누산기로 생각할 수 있습니다. 블록의 각 실행 결과는 누산기에 저장되고 다음 블록 실행으로 전달됩니다. 위에 표시된 코드의 경우 누산기의 결과를 0으로 기본 설정합니다. 블록을 실행할 때마다 주어진 숫자가 현재 총계에 추가 된 다음 결과가 누산기에 다시 저장됩니다. 다음 블록 호출에는이 새로운 값이 있으며 추가하고 다시 저장 한 후 반복합니다.

프로세스가 끝나면 inject는 누산기를 반환합니다.이 경우 배열의 모든 값의 합 또는 10입니다.

다음은 문자열 표현으로 키가 지정된 객체 배열에서 해시를 만드는 간단한 예입니다.

[1,"a",Object.new,:hi].inject({}) do |hash, item|
  hash[item.to_s] = item
  hash
end

이 경우 누적기를 빈 해시로 기본 설정 한 다음 블록이 실행될 때마다 채워집니다. 블록의 결과는 누산기에 다시 저장되므로 블록의 마지막 줄로 해시를 반환해야합니다.


그러나 OP가 제공 한 예제에서 반환되는 내용 (예 : 해시가 예제에 있음)에 대한 훌륭한 설명. 결과 + 설명으로 끝나고 반환 값이 있어야합니다.
Projjol

1
@Projjol은 result + explanation누산기로의 변환 및 반환 값입니다. 블록의 마지막 줄이므로 암시 적 반환입니다.
KA01

87

inject( 0예제에서) 로 시작하는 값 과 블록 을 취하고 목록의 각 요소마다 해당 블록을 한 번 실행합니다.

  1. 첫 번째 반복에서는 시작 값으로 제공 한 값과 목록의 첫 번째 요소를 전달하고 블록이 반환 한 값을 저장합니다 (이 경우 result + element )을 저장합니다.
  2. 그런 다음 블록을 다시 실행하여 첫 번째 반복의 결과를 첫 번째 인수로, 목록의 두 번째 요소를 두 번째 인수로 전달하여 결과를 다시 저장합니다.
  3. 목록의 모든 요소를 ​​사용할 때까지이 방법을 계속합니다.

이를 설명하는 가장 쉬운 방법은 예를 들어 각 단계의 작동 방식을 보여주는 것입니다. 다음은이 결과를 평가할 수있는 가상 단계입니다.

[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

단계를 작성해 주셔서 감사합니다. 이것은 많은 도움이되었습니다. 아래 다이어그램이 인젝션 메소드가 인젝션 인수로 전달 된 방식으로 구현되는 방식이라는 것을 의미하는지 여부에 대해 약간 혼란 스러웠습니다.

2
아래 도면은이 방법에 기초 할 수 구현할; 반드시 정확하게 이런 식으로 구현되는 것은 아닙니다. 이것이 제가 상상의 단계라고 말한 이유입니다. 기본 구조를 보여 주지만 정확한 구현은 아닙니다.
Brian Campbell

27

inject 메소드의 구문은 다음과 같습니다.

inject (value_initial) { |result_memo, object| block }

위의 예를 해결해 봅시다.

[1, 2, 3, 4].inject(0) { |result, element| result + element }

출력으로 10 을 제공합니다 .

시작하기 전에 각 변수에 저장된 값이 무엇인지 살펴 보겠습니다.

결과 = 0 0은 inject (value) 에서 온 0입니다.

element = 1 배열의 첫 번째 요소입니다.

승인!!! 위의 예를 이해해 봅시다

1 단계 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }

2 단계 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }

단계 : 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }

단계 : 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }

단계 : 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }

여기서 굵은 기울임 꼴 값은 배열에서 가져온 요소이며 간단히 굵은 체 값이 결과 값입니다.

#inject방법의 작동을 이해하기를 바랍니다 #ruby.


19

이 코드는 배열 내 4 개의 요소를 반복하고 이전 결과를 현재 요소에 추가합니다.

  • 1 + 2 = 3
  • 3 + 3 = 6
  • 6 + 4 = 10

15

그들이 말한 내용이지만 항상 "시작 값"을 제공 할 필요는 없습니다.

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

와 같다

[1, 2, 3, 4].inject { |result, element| result + element } # => 10

한번 해봐, 기다릴게

주입 할 인수가 전달되지 않으면 처음 요소가 첫 번째 반복으로 전달됩니다. 위의 예에서 결과는 1이고 요소는 2입니다. 따라서 블록에 대한 호출이 한 번 덜 이루어집니다.


14

주입 () 안에 넣은 숫자는 시작 위치를 나타내며 0 또는 1000 일 수 있습니다. 파이프 내부에는 2 개의 자리 표시 자 | x, y |가 있습니다. x = .inject ( 'x') 내부의 숫자와 secound는 객체의 각 반복을 나타냅니다.

[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15

1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15


6

주입은 블록을 적용

result + element

배열의 각 항목에. 다음 항목 ( "element")의 경우 블록에서 반환 된 값은 "result"입니다. 매개 변수를 사용하여 호출 한 방식으로 "result"는 해당 매개 변수의 값으로 시작합니다. 효과는 요소를 더하는 것입니다.


6

tldr; 한 가지 중요한 방식 inject과 다릅니다 . 블록의 마지막 실행 값을 반환하고 반복 된 배열을 반환합니다.mapinjectmap

그보다 많은 각 블록 실행 값은 첫 번째 매개 변수 ( result이 경우) 를 통해 다음 실행으로 전달되어 해당 값 ( (0)부분)을 초기화 할 수 있습니다 .

위 예제는 다음 map과 같이 쓸 수 있습니다 :

result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10

동일한 효과이지만 inject더 간결합니다.

map블록 에서 할당이 수행 되는 반면 블록에서 평가가 수행되는 경우가 많습니다 inject.

선택하는 방법은 원하는 범위에 따라 다릅니다 result. 때 하지 가이 같은 것 사용

result = [1, 2, 3, 4].inject(0) { |x, element| x + element }

"Lookie me, 방금 모든 것을 한 줄로 결합했습니다"라고 생각할 수도 있지만 x이미 result작업 해야했기 때문에 필요하지 않은 스크래치 변수로 메모리를 임시로 할당 했습니다.


4
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

다음과 같습니다.

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

3

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

평범한 영어로, 당신은이 배열 ( [1,2,3,4])을 거치게 됩니다. 4 개의 요소 (1, 2, 3 및 4)가 있으므로이 배열을 4 번 반복합니다. inject 메소드에는 1 개의 인수 (숫자 0)가 있으며이 인수를 첫 번째 요소 (0 + 1)에 추가합니다. 1과 같습니다. 1은 "결과"에 저장됩니다. 그런 다음 해당 결과 (1)를 다음 요소 (1 + 2)에 추가합니다. 이것은 3입니다. 이것은 이제 결과로 저장됩니다. 3 + 3은 6과 같습니다. 마지막으로 6 + 4는 10과 같습니다.


2

이 코드는 시작 값을 전달할 수는 없지만 진행 상황을 설명하는 데 도움이 될 수 있습니다.

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

1

여기서 시작한 다음 차단하는 모든 방법을 검토하십시오. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject

당신을 혼란스럽게하는 블록입니까, 왜 메소드에 가치가 있습니까? 그래도 좋은 질문입니다. 거기에 연산자 방법은 무엇입니까?

result.+

무엇으로 시작합니까?

#inject(0)

우리는 이것을 할 수 있습니까?

[1, 2, 3, 4].inject(0) { |result, element| result.+ element }

이 작동합니까?

[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }

당신은 단순히 배열의 모든 요소를 ​​합산하고 문서에서 볼 수있는 메모에 숫자를 산출한다는 아이디어에 기초하고 있습니다.

당신은 항상 이것을 할 수 있습니다

 [1, 2, 3, 4].each { |element| p element }

배열의 열거 가능한 항목이 반복되는지 확인하십시오. 이것이 기본 아이디어입니다.

주입하거나 줄이면 보내지는 메모 또는 누산기를 줄 수 있습니다.

우리는 결과를 얻을 수 있습니다

[1, 2, 3, 4].each { |result = 0, element| result + element }

그러나 아무것도 다시 나타나지 않으므로 이전과 동일하게 작동합니다.

[1, 2, 3, 4].each { |result = 0, element| p result + element }

요소 검사기 블록에서.


1

이것은 간단하고 이해하기 쉬운 설명입니다.

처음에는 다소 혼란 스럽기 때문에 "초기 값"은 잊어 버리십시오.

> [1,2,3,4].inject{|a,b| a+b}
=> 10

당신은 위와 같이 이해할 수 있습니다 : 1,2,3,4 사이에 "추가 기계"를 주입하고 있습니다. 즉, 1 ♫ 2 ♫ 3 ♫ 4이고 ♫는 추가 기계이므로 1 + 2 + 3 + 4와 동일하며 10입니다.

실제로 +그들 사이에 a 를 삽입 할 수 있습니다 :

> [1,2,3,4].inject(:+)
=> 10

마치 +1,2,3,4 사이에 a 를 삽입하여 1 + 2 + 3 + 4로 만들고 10으로 만듭니다. 이것은 :+루비 +가 기호 형태로 지정하는 방식입니다 .

이것은 이해하기 쉽고 직관적입니다. 그리고 단계별로 작동하는 방식을 분석하려면 다음과 같습니다 .1과 2를 가져 와서 추가하고 결과가있을 때 먼저 저장하고 (3) 저장하고 다음은 저장됩니다 값 3과 배열 요소 3은 6 인 a + b 프로세스를 거치고 이제이 값을 저장하고 이제 6과 4는 a + b 프로세스를 거치고 10입니다.

((1 + 2) + 3) + 4

"초기 값" 0은 시작하기위한 "기본"일뿐입니다. 많은 경우에 필요하지 않습니다. 1 * 2 * 3 * 4가 필요하다고 상상해보십시오.

[1,2,3,4].inject(:*)
=> 24

그리고 그것은 끝났습니다. 1전체를에 곱하기 위해 "초기 값"이 필요하지 않습니다 1.


0

.inject () 메소드의 또 다른 형태가 있습니다. [4,5] .inject (& : +)이 부분의 모든 요소를 ​​더할 것입니다



당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.