इंजेक्शन विधि की एक सरल व्याख्या की आवश्यकता है


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

मैं इस कोड को देख रहा हूं लेकिन मेरा दिमाग यह दर्ज नहीं कर रहा है कि संख्या 10 कैसे परिणाम बन सकती है। क्या कोई समझाता है कि यहाँ क्या हो रहा है?

ruby  syntax 

3
देखें विकिपीडिया: फोल्ड (उच्च-क्रम फ़ंक्शन) : इंजेक्शन एक "गुना बाएं" है, हालांकि (दुर्भाग्य से) अक्सर रूबी उपयोग में साइड-इफेक्ट्स के साथ।
user2864740

जवाबों:


208

आप पहले ब्लॉक तर्क को एक संचायक के रूप में सोच सकते हैं: ब्लॉक के प्रत्येक रन का परिणाम संचायक में संग्रहीत किया जाता है और फिर ब्लॉक के अगले निष्पादन के लिए पारित किया जाता है। ऊपर दिखाए गए कोड के मामले में, आप संचायक को डिफ़ॉल्ट कर रहे हैं, परिणाम के लिए। ब्लॉक के प्रत्येक रन में दिए गए नंबर को वर्तमान कुल में जोड़ता है और फिर परिणाम को संचायक में वापस संग्रहीत करता है। अगले ब्लॉक कॉल में यह नया मूल्य है, इसे जोड़ता है, इसे फिर से संग्रहीत करता है, और दोहराता है।

प्रक्रिया के अंत में, इंजेक्टर संचायक को लौटाता है, जो इस मामले में सरणी में सभी मानों का योग है, या 10 है।

उनके स्ट्रिंग प्रतिनिधित्व द्वारा कुंजीबद्ध वस्तुओं के एक समूह से हैश बनाने के लिए यहां एक और सरल उदाहरण दिया गया है:

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

इस स्थिति में, हम अपने संचायक को एक खाली हैश में डिफॉल्ट कर रहे हैं, फिर प्रत्येक बार ब्लॉक निष्पादित होने पर इसे पॉपुलेट कर रहा है। ध्यान दें कि हमें ब्लॉक की अंतिम पंक्ति के रूप में हैश लौटना चाहिए, क्योंकि ब्लॉक का परिणाम संचायक में वापस संग्रहीत किया जाएगा।


महान विवरण, हालांकि, ओपी द्वारा दिए गए उदाहरण में, क्या लौटाया जा रहा है (जैसे हैश आपके उदाहरण में है)। यह परिणाम + स्पष्टीकरण के साथ समाप्त होता है और वापसी मूल्य होना चाहिए, हाँ?
प्रोज्जोल

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
नीचे दिए गए आरेख इस पर आधारित है कि इसे कैसे लागू किया जा सकता है; यह जरूरी इस तरह से लागू नहीं है। इसलिए मैंने कहा कि यह चरणों का एक काल्पनिक सेट है; यह मूल संरचना को प्रदर्शित करता है, लेकिन सटीक कार्यान्वयन नहीं।
ब्रायन कैंपबेल

27

इंजेक्शन विधि के लिए वाक्य रचना इस प्रकार है:

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

आइए उपरोक्त उदाहरण को हल करें

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

जो आउटपुट के रूप में 10 देता है ।

तो, शुरू करने से पहले आइए देखें कि प्रत्येक चर में क्या मूल्य संचित हैं:

परिणाम = 0 शून्य इंजेक्शन (मान) से आया है जो 0 है

तत्व = 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 }

चरण: ५ [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

कोड सरणी के भीतर चार तत्वों से अधिक पुनरावृत्त करता है और पिछले परिणाम को वर्तमान तत्व में जोड़ता है:

  • 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 हो सकता है। पाइप के अंदर आपके पास दो स्थान धारक हैं। x, y | x = .inject ('x') के अंदर आपके पास कितनी संख्या थी, और एकांत आपकी वस्तु के प्रत्येक पुनरावृत्ति का प्रतिनिधित्व करता है।

[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

सरणी में प्रत्येक आइटम के लिए। अगले आइटम ("तत्व") के लिए, ब्लॉक से लौटाया गया मान "परिणाम" है। जिस तरह से आपने इसे (एक पैरामीटर के साथ) कहा है, "परिणाम" उस पैरामीटर के मूल्य से शुरू होता है। तो प्रभाव तत्वों को जोड़ रहा है।


6

tldr; एक महत्वपूर्ण तरीके injectसे अलग है map: injectजबकि ब्लॉक के अंतिम निष्पादन का मूल्य लौटाता हैmap लौटाता है, इसके द्वारा पुनरावृत्त सरणी को लौटाता है।

इससे अधिक प्रत्येक ब्लॉक निष्पादन का मान पहले पैरामीटर ( 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 }

आप सभी की तरह हो सकते हैं, "मुझे देखो, मैंने बस उस एक लाइन में सभी को मिला दिया," लेकिन आपने अस्थायी रूप से मेमोरी 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 बार पुनरावृत्ति करेंगे, क्योंकि 4 तत्व (1, 2, 3 और 4) हैं। इंजेक्शन विधि में 1 तर्क (संख्या 0) है, और आप उस तर्क को 1 तत्व (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 है और machine एक जोड़ने वाली मशीन है, इसलिए यह 1 + 2 + 3 + 4 के समान है, और यह 10 है।

आप वास्तव +में उनके बीच में एक इंजेक्शन लगा सकते हैं:

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

और यह +1,2,3,4 के बीच में इंजेक्ट होता है , जिससे यह 1 + 2 + 3 + 4 हो जाता है और 10. यह :+रूबी को निर्दिष्ट करने का तरीका है+ प्रतीक के रूप में ।

यह समझने में काफी आसान और सहज है। और यदि आप विश्लेषण करना चाहते हैं कि यह कदम से कदम कैसे काम करता है, तो यह है: 1 और 2 लेना, और अब उन्हें जोड़ें, और जब आपके पास एक परिणाम होता है, तो इसे पहले स्टोर करें (जो 3 है), और अब, अगला संग्रहीत है मान 3 और सरणी तत्व 3 एक + b प्रक्रिया से गुजर रहा है, जो 6 है, और अब इस मान को संग्रहीत करते हैं, और अब 6 और 4 एक + b प्रक्रिया से गुजरते हैं, और 10. आप अनिवार्य रूप से कर रहे हैं

((1 + 2) + 3) + 4

और 10. "प्रारंभिक मूल्य" के 0साथ शुरू करने के लिए सिर्फ एक "आधार" है। कई मामलों में, आपको इसकी आवश्यकता नहीं है। कल्पना करें कि आपको 1 * 2 * 3 * 4 की आवश्यकता है और यह है

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

और हो गया। आपको 1संपूर्ण चीज़ को गुणा करने के लिए "प्रारंभिक मान" की आवश्यकता नहीं है 1



0

यह सिर्फ reduceया fold, यदि आप अन्य भाषाओं से परिचित हैं।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.