2015-07-08 15 views
7

के माध्यम से इसे फिर से चालू करते समय Array से तत्वों को सुरक्षित रूप से हटाना संभव है? एक पहला परीक्षण आशाजनक दिखता है:क्या प्रत्येक के अंदर एक ऐरे से हटाना सुरक्षित है?

a = (1..4).to_a 
a.each { |i| a.delete(i) if i == 2 } 
# => [1, 3, 4] 

हालांकि, मैं पर मुश्किल तथ्यों नहीं पा सके: चाहे वह (डिजाइन) के द्वारा सुरक्षित है

  • जो रूबी संस्करण के बाद से यह सुरक्षित है

अतीत में कुछ बिंदुओं पर, ऐसा लगता है कि यह not possible to do था:

यह काम नहीं कर रहा है क्योंकि रूबी कुछ हटाने की कोशिश करते समय .each पाश से बाहर निकलता है।

documentation पुनरावृत्ति के दौरान विलुप्त होने के बारे में कुछ भी नहीं बताता है।

मैं reject या delete_if की तलाश नहीं कर रहा हूं। मैं चीजों को एक सरणी के तत्वों के साथ करना चाहता हूं, और कभी-कभी सरणी से एक तत्व भी हटा देता हूं (जब मैंने कहा तत्व के साथ अन्य चीजें की हैं)।

अद्यतन 1: मैं "सुरक्षित" की मेरी परिभाषा पर बहुत स्पष्ट नहीं था, मैं क्या मतलब था:

  • किसी भी अपवाद नहीं उठता
  • Array
  • में किसी भी तत्व को ना छोड़ें
+1

यह सुरक्षित नहीं है (आप "सुरक्षित" को परिभाषित करने के आधार पर) उन सभी भाषाओं में जिनके साथ मेरा अच्छा अनुभव है। इसे पुन: सक्रिय करते समय संग्रह को संशोधित करना आपके प्रोग्राम को तोड़ने या आपको आश्चर्यचकित करने की गारंटी है।इसके लिए एक वैध उपयोग मामले के बारे में नहीं सोच सकते हैं। –

+0

इसे सुरक्षित करने के लिए, मूल –

उत्तर

7

आपको अनधिकृत उत्तरों पर भरोसा नहीं करना चाहिए। आपके द्वारा उद्धृत उत्तर गलत है, जैसा कि केविन की टिप्पणी ने इंगित किया है।

यह each के दौरान एरे से तत्वों को हटाने के लिए सुरक्षित है (रूबी की शुरुआत से) जबकि रूबी ऐसा करने में कोई त्रुटि नहीं उठाएगा, और एक निर्णायक (यानी यादृच्छिक नहीं) परिणाम देगा।

हालांकि, आपको सावधान रहना होगा क्योंकि जब आप कोई तत्व हटाते हैं, तो उसके बाद वाले तत्वों को स्थानांतरित कर दिया जाएगा, इसलिए तत्व जिसे अगले पुनरावृत्त किया जाना चाहिए, हटाए गए तत्व की स्थिति में ले जाया जाएगा, जो कि किया गया है पहले से ही पुनरावृत्त, और छोड़ दिया जाएगा।

+1

को मैंने जो कुछ कहा था उसे चुनौती देना चाहता था, लेकिन मेरे उदाहरण कोड में 'रखता हूं;' जोड़कर मैं वास्तव में सत्यापित कर सकता था कि वास्तव में अगला तत्व "छोड़ा गया" है। इसका मतलब है कि मुझे अपनी सरणी को डुप्लिकेट करना होगा और कॉपी पर फिर से भरना होगा। – NobodysNightmare

+1

@ नोबोडिस नाइटमेयर: "मैं जो कहा था उसे चुनौती देना चाहता था" - आम तौर पर, टैग के लिए सोने के बैज वाले उपयोगकर्ता जानते हैं कि वे किस बारे में बात कर रहे हैं। :) –

1

मैं कहूँगा कि यह सुरक्षित है, निम्नलिखित के आधार पर:

2.2.2 :035 > a = (1..4).to_a 
=> [1, 2, 3, 4] 
2.2.2 :036 > a.each { |i| a.delete(i+1) if i > 1 ; puts i } 
1 
2 
4 
=> [1, 2, 4] 

मैं इस परीक्षण से अनुमान लगाता हूं कि रूबी सही ढंग से पहचानता है कि सामग्री "3" को हटा दिया गया है, जबकि तत्व "3" को हटाया गया है जबकि तत्व "2" संसाधित किया जा रहा है, अन्यथा तत्व "4" भी हटा दिया जाएगा।

हालांकि,

2.2.2 :040 > a.each { |i| puts i; a.delete(i) if i > 1 ; puts i } 
1 
1 
2 
2 
4 
4 

यह पता चलता है "2" के बाद हटा दिया गया है, अगले तत्व संसाधित जो भी अब तीसरे सरणी में है, इसलिए तत्व यह है कि तीसरे स्थान पर हुआ करता था संसाधित नहीं है कि बिलकुल। प्रत्येक पुनरावृत्ति पर प्रक्रिया करने के लिए अगले तत्व को खोजने के लिए प्रत्येक सरणी को फिर से जांचना प्रतीत होता है।

मुझे लगता है कि इस बात को ध्यान में रखते हुए, आपको प्रसंस्करण से पहले अपनी परिस्थितियों में सरणी को डुप्लिकेट करना चाहिए।

+0

से तत्वों को हटाते समय, सरणी के _copy_ को फिर से सक्रिय करें, जैसे ही आप उस तत्व को हटाते हैं जिसे आप वर्तमान में देख रहे हैं (यानी 'i',' i + 1' के बजाय), पुनरावृत्ति भविष्य के तत्वों को छोड़ दें। यही कारण है कि आपका उदाहरण कोड काम करता है: आप भविष्य के तत्व को हटाते हैं, जिसे (जैसा कि मैंने अभी सीखा है) को बेकार ढंग से काम करना चाहिए। – NobodysNightmare

+0

वैसे यह प्रभावी रूप से उस तत्व को छोड़ देगा जो सरणी में अगला था, बिल्कुल भविष्य के सभी तत्व नहीं। यह अभी भी उन तत्वों को संसाधित करेगा जो वर्तमान स्थिति में बाद की स्थिति में हैं। –

0

यह निर्भर करता है।

सभी .each एक गणक देता है, जिसमें संग्रह को एक पॉइंटर रखा जाता है जहां यह छोड़ा जाता है। उदाहरण:

a = [1,2,3] 
b = a.each # => #<Enumerator: [1, 2, 3]:each> 
b.next # => 1 
a.delete(2) 
b.next # => 3 
a.clear 
b.next # => StopIteration: iteration reached an end 

प्रत्येक ब्लॉक के साथ जब तक यात्रा अपने अंत तक पहुँच जाता है next कहता है। इसलिए जब तक आप किसी भी 'भविष्य' सरणी रिकॉर्ड को संशोधित नहीं करते हैं, तब तक यह सुरक्षित होना चाहिए।

हालांकि रूबी के Enumerable और Array में बहुत उपयोगी तरीके हैं, आपको वास्तव में ऐसा करने की आवश्यकता नहीं है।

+0

"तो जब तक आप संशोधित नहीं करते हैं ... यह सुरक्षित होना चाहिए" ओपी पूछ रहा है कि क्या होता है जब आप ** ** संशोधित करते हैं। आपका जवाब व्यर्थ है। और आपका उल्लेख है कि 'प्रत्येक' के गणक उपयोग का उपयोग ओपी के बिंदु से संबंधित प्रतीत नहीं होता है। – sawa

+0

मैंने बस एक उदाहरण के साथ समझाया कि कैसे भविष्य के रिकॉर्ड हटाना सरणी को बदलता है, लेकिन मुझे लगता है कि प्रत्येक अपने –

0

आप सही हैं, अतीत में यह सलाह दी गई थी कि संग्रह से वस्तुओं को हटाने के दौरान इसे हटाया जाए। मेरे परीक्षणों में और कम से कम संस्करण 1.9.3 के साथ एक सरणी में अभ्यास में यह कोई समस्या नहीं देता है, भले ही पहले या अगले तत्वों को हटाया जाए।

यह मेरी राय है कि आप नहीं कर सकते हैं। तत्वों को अस्वीकार करने और एक नई सरणी को असाइन करने के लिए एक और स्पष्ट और सुरक्षित दृष्टिकोण है।

b = a.reject{ |i| i == 2 } #[1, 3, 4] 

आप अपने एक सरणी भी संभव है कि पुन: उपयोग करना चाहते मामले में

a = a.reject{ |i| i == 2 } #[1, 3, 4] 

जो वास्तव में के रूप में

a.reject!{ |i| i == 2 } #[1, 3, 4] 

ही है आप कहते हैं कि आप का उपयोग नहीं करना चाहते हैं अस्वीकार करें क्योंकि आप हटाने से पहले तत्वों के साथ अन्य चीजें करना चाहते हैं, लेकिन यह भी संभव है।

a.reject!{ |i| puts i if i == 2;i == 2 } 
# 2 
#[1, 3, 4] 
3

आदेश में अपने प्रश्न का उत्तर है, चाहे वह ऐसा करने के लिए "सुरक्षित" है, तो आप पहले आप "सुरक्षित" द्वारा क्या मतलब है परिभाषित करने के लिए होगा। क्या आपका मतलब है

  1. यह रनटाइम को क्रैश नहीं करता है?
  2. यह raiseException नहीं है?
  3. raise एक Exception है?
  4. यह दृढ़ता से व्यवहार करता है?
  5. क्या आप ऐसा करने की अपेक्षा करते हैं? (क्या कर आप इसे करने की उम्मीद?)

दुर्भाग्य से, रूबी भाषा विशिष्टता वास्तव में उपयोगी नहीं है:

15.2.12.5।10 सरणी # प्रत्येक


each (& ब्लॉक)


दृश्यता: सार्वजनिक

व्यवहार:

    ,210
  • ब्लॉक दिया जाता है:
    1. अनुक्रमण क्रम में रिसीवर के प्रत्येक तत्व के लिए, केवल तर्क के रूप में तत्व के साथ ब्लॉक कहते हैं।
    2. रिसीवर लौटें।

यह सूचित करते हैं कि यह वास्तव में 1., 2., 4., और 5 से ऊपर के अर्थ में पूरी तरह से सुरक्षित है लगता है।

The documentation का कहना है:

each { |item| block }ary

self में दिए गए ब्लॉक प्रत्येक तत्व के लिए एक बार कॉल, एक पैरामीटर के रूप में उस तत्व गुजर।

फिर से, यह कल्पना के समान ही प्रतीत होता है।

दुर्भाग्यवश, वर्तमान में मौजूदा रूबी कार्यान्वयन के इस तरह से spec की व्याख्या नहीं करते हैं।

क्या वास्तव में एमआरआई और YARV में होता है निम्नलिखित है: सरणी उत्परिवर्तन, तत्वों के किसी भी स्थानांतरण सहित और/या हो जाता सूचकांक तुरंत दिखाई है, जो पर आधारित है इटरेटर कोड की आंतरिक कार्यान्वयन सहित सरणी सूचकांक। इसलिए, यदि आप में या आप वर्तमान में पुनरावृत्ति कर रहे हैं स्थिति से पहले एक तत्व हटाने के बाद आप अगले तत्व को छोड़ देगा, जबकि अगर आप स्थिति आप वर्तमान में पुनरावृत्ति कर रहे हैं के बाद एक तत्व हटाते हैं, तो आप कि तत्व को छोड़ देगा। each_with_index के लिए, आप यह भी देखेंगे कि हटाए गए तत्व के बाद सभी तत्वों में उनके सूचकांक स्थानांतरित हो गए हैं (या बल्कि दूसरी तरफ: इंडेक्स बने रहें, लेकिन तत्वों को स्थानांतरित किया गया है)।

तो, यह व्यवहार "सुरक्षित" 1., 2. के अर्थ में है, और 4.

अन्य रूबी कार्यान्वयन ज्यादातर प्रतिलिपि इस (गैर-दस्तावेजी) व्यवहार है, लेकिन गैर-दस्तावेजी किया जा रहा है, तो आप भरोसा नहीं कर सकते इस पर, और वास्तव में, मेरा मानना ​​है कि कम से कम एक ने प्रयोगात्मक रूप से कुछ प्रकार के ConcurrentModificationException को बढ़ाने के साथ प्रयोग किया था।

+0

विस्तृत उत्तर के लिए धन्यवाद। मैं वास्तव में 5 की तरह कुछ की उम्मीद कर रहा था, मेरी उम्मीद थी: तत्व हटा दिया जाएगा, लेकिन किसी भी मामले में कोई तत्व छोड़ा नहीं गया है। – NobodysNightmare

संबंधित मुद्दे