[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Tôi đang xem mã này nhưng bộ não của tôi không đăng ký làm thế nào số 10 có thể trở thành kết quả. Ai đó có thể giải thích những gì đang xảy ra ở đây?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Tôi đang xem mã này nhưng bộ não của tôi không đăng ký làm thế nào số 10 có thể trở thành kết quả. Ai đó có thể giải thích những gì đang xảy ra ở đây?
Câu trả lời:
Bạn có thể nghĩ về đối số khối đầu tiên là một bộ tích lũy: kết quả của mỗi lần chạy của khối được lưu trữ trong bộ tích lũy và sau đó được chuyển sang lần thực hiện tiếp theo của khối. Trong trường hợp mã được hiển thị ở trên, bạn đang mặc định bộ tích lũy, kết quả là 0. Mỗi lần chạy của khối sẽ thêm số đã cho vào tổng hiện tại và sau đó lưu lại kết quả vào bộ tích. Cuộc gọi khối tiếp theo có giá trị mới này, thêm vào nó, lưu lại và lặp lại.
Khi kết thúc quá trình, tiêm trả về bộ tích lũy, trong trường hợp này là tổng của tất cả các giá trị trong mảng, hoặc 10.
Đây là một ví dụ đơn giản khác để tạo hàm băm từ một mảng các đối tượng, được khóa bởi biểu diễn chuỗi của chúng:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
Trong trường hợp này, chúng tôi đang mặc định bộ tích lũy của mình thành một hàm băm trống, sau đó điền vào nó mỗi khi khối thực thi. Lưu ý rằng chúng ta phải trả về hàm băm là dòng cuối cùng của khối, vì kết quả của khối sẽ được lưu lại trong bộ tích lũy.
result + explanation
là cả chuyển đổi cho tích lũy và giá trị trả về. Đây là dòng cuối cùng trong khối làm cho nó trở lại ngầm.
inject
lấy một giá trị để bắt đầu ( 0
trong ví dụ của bạn) và một khối và nó chạy khối đó một lần cho mỗi thành phần của danh sách.
result + element
).Cách dễ nhất để giải thích điều này có thể là chỉ ra cách mỗi bước hoạt động, ví dụ của bạn; đây là một tập hợp các bước tưởng tượng cho thấy kết quả này có thể được đánh giá như thế nào:
[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
Cú pháp của phương thức tiêm như sau:
inject (value_initial) { |result_memo, object| block }
Hãy giải quyết ví dụ trên tức là
[1, 2, 3, 4].inject(0) { |result, element| result + element }
cung cấp cho 10 là đầu ra.
Vì vậy, trước khi bắt đầu, hãy xem các giá trị được lưu trữ trong mỗi biến là gì:
result = 0 Số 0 đến từ chích (giá trị) bằng 0
phần tử = 1 Đây là phần tử đầu tiên của mảng.
Ôi !!! Vì vậy, hãy bắt đầu hiểu ví dụ trên
Bước 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Bước 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Bước 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Bước 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Bước: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Ở đây các giá trị Bold-Italic là các phần tử tìm nạp từ mảng và Bold đơn giản giá trị là các giá trị kết quả.
Tôi hy vọng rằng bạn hiểu hoạt động của #inject
phương pháp #ruby
.
Mã lặp lại qua bốn phần tử trong mảng và thêm kết quả trước đó vào phần tử hiện tại:
Những gì họ nói, nhưng cũng lưu ý rằng không phải lúc nào bạn cũng cần cung cấp "giá trị bắt đầu":
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
giống như
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Hãy thử nó, tôi sẽ chờ.
Khi không có đối số nào được truyền vào để tiêm, hai phần tử đầu tiên được truyền vào lần lặp đầu tiên. Trong ví dụ trên, kết quả là 1 và phần tử là 2 lần đầu tiên, do đó, một cuộc gọi ít hơn được thực hiện cho khối.
Số bạn đặt bên trong () tiêm của bạn đại diện cho một nơi bắt đầu, nó có thể là 0 hoặc 1000. Bên trong các đường ống bạn có hai người giữ chỗ | x, y |. x = số bao giờ bạn có trong .inject ('x') và ẩn số đại diện cho mỗi lần lặp của đối tượng của bạn.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Tiêm áp dụng khối
result + element
đến từng mục trong mảng. Đối với mục tiếp theo ("phần tử"), giá trị được trả về từ khối là "kết quả". Cách bạn đã gọi nó (với một tham số), "kết quả" bắt đầu bằng giá trị của tham số đó. Vì vậy, hiệu quả là thêm các yếu tố lên.
tldr; inject
khác với map
một cách quan trọng: inject
trả về giá trị của lần thực hiện cuối cùng của khối trong khimap
trả về mảng được lặp lại.
Hơn thế nữa, giá trị của mỗi lần thực hiện khối được chuyển sang lần thực hiện tiếp theo thông qua tham số đầu tiên ( result
trong trường hợp này) và bạn có thể khởi tạo giá trị đó ((0)
phần).
Ví dụ trên của bạn có thể được viết bằng cách sử dụng map
như thế này:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
Hiệu ứng tương tự nhưng inject
ngắn gọn hơn ở đây.
Bạn sẽ thường thấy một bài tập xảy ra trong map
khối, trong khi đánh giá xảy ra trong inject
khối.
Phương pháp bạn chọn phụ thuộc vào phạm vi bạn muốn result
. Khi không sử dụng nó sẽ là một cái gì đó như thế này:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
Bạn có thể giống như tất cả, "Hãy nhìn tôi, tôi chỉ kết hợp tất cả thành một dòng", nhưng bạn cũng tạm thời phân bổ bộ nhớ cho x
một biến số không cần thiết vì bạn đã phải result
làm việc với.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
tương đương như sau:
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
Trong tiếng Anh đơn giản, bạn đang trải qua (lặp lại) thông qua mảng này ( [1,2,3,4]
). Bạn sẽ lặp lại qua mảng này 4 lần, bởi vì có 4 phần tử (1, 2, 3 và 4). Phương thức tiêm có 1 đối số (số 0) và bạn sẽ thêm đối số đó vào phần tử thứ 1 (0 + 1. Điều này bằng 1). 1 được lưu trong "kết quả". Sau đó, bạn thêm kết quả đó (là 1) vào phần tử tiếp theo (1 + 2. Đây là 3). Điều này bây giờ sẽ được lưu lại như là kết quả. Tiếp tục đi: 3 + 3 bằng 6. Và cuối cùng, 6 + 4 bằng 10.
Mã này không cho phép khả năng không vượt qua giá trị bắt đầu, nhưng có thể giúp giải thích những gì đang diễn ra.
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
Bắt đầu ở đây và sau đó xem xét tất cả các phương pháp có khối. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
Đây có phải là khối làm bạn bối rối hoặc tại sao bạn có một giá trị trong phương thức? Câu hỏi hay mặc dù. Phương pháp toán tử ở đó là gì?
result.+
Nó bắt đầu như thế nào?
#inject(0)
Chúng ta có thể làm điều này?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
Nó có hoạt động không?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
Bạn thấy tôi đang xây dựng ý tưởng rằng nó chỉ đơn giản là tổng hợp tất cả các yếu tố của mảng và mang lại một số trong bản ghi nhớ mà bạn thấy trong các tài liệu.
Bạn luôn có thể làm điều này
[1, 2, 3, 4].each { |element| p element }
để xem vô số các mảng được lặp qua. Đó là ý tưởng cơ bản.
Chỉ cần tiêm hoặc giảm cung cấp cho bạn một bản ghi nhớ hoặc bộ tích lũy được gửi đi.
Chúng tôi có thể cố gắng để có được một kết quả
[1, 2, 3, 4].each { |result = 0, element| result + element }
nhưng không có gì trở lại vì vậy điều này chỉ hoạt động giống như trước đây
[1, 2, 3, 4].each { |result = 0, element| p result + element }
trong khối thanh tra phần tử.
Đây là một lời giải thích đơn giản và khá dễ hiểu:
Hãy quên đi "giá trị ban đầu" vì nó hơi khó hiểu khi bắt đầu.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
Bạn có thể hiểu như trên: Tôi đang tiêm một "máy thêm" vào giữa 1,2,3,4. Có nghĩa, nó là 1 ♫ 2 ♫ 3 4 và là một máy thêm, vì vậy nó giống như 1 + 2 + 3 + 4, và nó là 10.
Bạn thực sự có thể tiêm một +
giữa chúng:
> [1,2,3,4].inject(:+)
=> 10
và nó giống như, tiêm một +
khoảng giữa 1,2,3,4, biến nó thành 1 + 2 + 3 + 4 và là 10. Đây :+
là cách chỉ định của Ruby+
dưới dạng biểu tượng.
Điều này khá dễ hiểu và trực quan. Và nếu bạn muốn phân tích cách thức hoạt động của nó từng bước, thì giống như: lấy 1 và 2, và bây giờ thêm chúng, và khi bạn có kết quả, hãy lưu trữ nó trước (là 3), và bây giờ, tiếp theo là lưu trữ giá trị 3 và phần tử mảng 3 trải qua quá trình a + b, là 6 và hiện lưu trữ giá trị này, và bây giờ 6 và 4 trải qua quá trình a + b và là 10. Về cơ bản, bạn đang làm
((1 + 2) + 3) + 4
và là 10. "Giá trị ban đầu" 0
chỉ là "cơ sở" để bắt đầu. Trong nhiều trường hợp, bạn không cần nó. Hãy tưởng tượng nếu bạn cần 1 * 2 * 3 * 4 và đó là
[1,2,3,4].inject(:*)
=> 24
và nó đã được thực hiện. Bạn không cần "giá trị ban đầu" 1
để nhân toàn bộ 1
.
Có một dạng khác của phương thức .inject () Rất hữu ích [4,5] .inject (&: +) Điều đó sẽ cộng tất cả các phần tử của khu vực
Giống như thế này:
[1,2,3,4].inject(:+)
=> 10