पर्ल हैश की कुंजी के माध्यम से पुनरावृति का सबसे सुरक्षित तरीका क्या है?


107

यदि मेरे पास (कुंजी, मान) जोड़े के एक समूह के साथ एक पर्ल हैश है, तो सभी कुंजियों के माध्यम से पुनरावृत्ति करने का पसंदीदा तरीका क्या है? मैंने सुना है कि eachकिसी तरह से उपयोग करने से अनपेक्षित दुष्प्रभाव हो सकते हैं। तो, क्या यह सच है, और दो तरीकों में से एक सबसे अच्छा है, या एक बेहतर तरीका है?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}

जवाबों:


199

अंगूठे का नियम आपकी आवश्यकताओं के लिए सबसे उपयुक्त फ़ंक्शन का उपयोग करना है।

यदि आप केवल कुंजियाँ चाहते हैं और किसी भी मान को पढ़ने की योजना नहीं बनाते हैं, तो कुंजियों का उपयोग करें ():

foreach my $key (keys %hash) { ... }

यदि आप केवल मान चाहते हैं, तो मानों का उपयोग करें ():

foreach my $val (values %hash) { ... }

यदि आपको कुंजी और मान चाहिए, तो प्रत्येक का उपयोग करें ():

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

यदि आप पुनरावृत्ति के दौरान वर्तमान कुंजी को हटाने के अलावा किसी भी तरह से हैश की चाबियाँ बदलने की योजना बनाते हैं , तो आपको प्रत्येक () का उपयोग नहीं करना चाहिए। उदाहरण के लिए, दोगुने मूल्यों के साथ अपरकेस कुंजी का एक नया सेट बनाने के लिए यह कोड कुंजियों का उपयोग करके ठीक काम करता है ():

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

अपेक्षित हैश का उत्पादन:

(a => 1, A => 2, b => 2, B => 4)

लेकिन एक ही काम करने के लिए प्रत्येक () का उपयोग करना:

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

कठिन-से-पूर्वानुमान तरीकों से गलत परिणाम उत्पन्न करता है। उदाहरण के लिए:

(a => 1, A => 2, b => 2, B => 8)

यह, हालांकि, सुरक्षित है:

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

यह सब पर्ल दस्तावेज़ में वर्णित है:

% perldoc -f keys
% perldoc -f each

6
कृपया एक शून्य-संदर्भ कुंजी% h जोड़ें; प्रत्येक लूप से पहले पुनरावृत्ति का उपयोग करके सुरक्षित रूप से दिखाने के लिए।
ysth

5
प्रत्येक के साथ एक और कैवेट है। पुनरावृत्त हैश से जुड़ा है, संदर्भ से नहीं, जिसका अर्थ है कि यह फिर से प्रवेश नहीं है। उदाहरण के लिए यदि आप एक हैश पर लूप करते हैं, और हैश पर्ल को आंतरिक रूप से पुनरावृत्तिकर्ता को रीसेट करेंगे, तो यह कोड लूप को अंतहीन बना देगा: मेरा% हैश = (a => 1, b => 2, c => 3); जबकि (मेरी ($ k, $ v) = प्रत्येक% हैश) {प्रिंट% हैश; } अधिक पढ़ें blogs.perl.org/users/rurban/2014/04/do-not-use-each.html
Rawler

28

उपयोग करते समय एक बात जो आपको पता होनी चाहिए eachकि यह आपके हैश में "राज्य" जोड़ने का दुष्प्रभाव है (हैश को यह याद रखना होगा कि "अगली" कुंजी क्या है)। ऊपर पोस्ट किए गए स्निपेट्स जैसे कोड का उपयोग करते समय, जो एक ही बार में पूरे हैश पर पुनरावृति करता है, यह आमतौर पर कोई समस्या नहीं है। हालाँकि, आप समस्याओं को ट्रैक करने के लिए कड़ी मेहनत करेंगे (मैं अनुभव से बात करता हूं;), जब आप सभी कुंजियों को संसाधित करने से पहले eachबयानों के साथ lastया लूप returnसे बाहर निकलने का उपयोग करते हैं while ... each

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

उदाहरण:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

यह प्रिंट:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

"बार" और बाज "की चाबियाँ क्या हुईं? वे अभी भी वहां हैं, लेकिन दूसरा eachशुरू होता है जहां पहले वाले ने छोड़ दिया, और जब वह हैश के अंत तक पहुंचता है, तो हम उन्हें दूसरे लूप में कभी नहीं देखते हैं।"


22

जिस स्थान पर eachआपको समस्याएं हो सकती हैं, वह यह है कि यह एक सच्चा, गैर-स्कॉप्ड इटेटर है। उदाहरण के माध्यम से:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

यदि आपको यह सुनिश्चित करने की आवश्यकता है कि eachसभी कुंजी और मान प्राप्त हो जाएं, तो आपको यह सुनिश्चित करने की आवश्यकता है कि आप उपयोग करते हैं keysया valuesपहले (जैसे कि पुनरावृत्त रीसेट करता है)। प्रत्येक के लिए प्रलेखन देखें ।


14

प्रत्येक सिंटैक्स का उपयोग करने से कुंजी के पूरे सेट को एक ही बार में उत्पन्न होने से रोका जा सकेगा। यह महत्वपूर्ण हो सकता है यदि आप लाखों पंक्तियों वाले डेटाबेस में टाई-एड हैश का उपयोग कर रहे हैं। आप एक साथ सभी कुंजियों की पूरी सूची तैयार नहीं करना चाहते हैं और अपनी भौतिक स्मृति को समाप्त नहीं करना चाहते हैं। इस मामले में प्रत्येक एक पुनरावृत्त के रूप में कार्य करता है जबकि कुंजी वास्तव में लूप शुरू होने से पहले पूरे सरणी को उत्पन्न करता है।

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

यदि स्मृति कोई समस्या नहीं है, तो आमतौर पर मानचित्र या कुंजियाँ प्रतिमान अधिक प्रचलित और प्रतिमान पढ़ने में आसान होती हैं।


6

इस विषय पर कुछ विविध विचार:

  1. खुद हैश पुनरावृत्तियों में से किसी के बारे में कुछ भी असुरक्षित नहीं है। असुरक्षित क्या हैश की कुंजियों को संशोधित कर रहा है, जबकि आप इसे देख रहे हैं। (यह मूल्यों को संशोधित करने के लिए पूरी तरह से सुरक्षित है।) केवल संभावित साइड-इफ़ेक्ट जो मैं सोच सकता हूं, वह यह है कि valuesएलियास लौटाता है जिसका अर्थ है कि उन्हें संशोधित करना हैश की सामग्री को संशोधित करेगा। यह डिजाइन द्वारा है, लेकिन कुछ परिस्थितियों में आप जो चाहते हैं वह नहीं हो सकता है।
  2. जॉन का स्वीकृत जवाब एक अपवाद के साथ अच्छा है: प्रलेखन स्पष्ट है कि हैश से अधिक पुनरावृत्ति करते समय चाबियाँ जोड़ना सुरक्षित नहीं है। यह कुछ डेटा सेट के लिए काम कर सकता है, लेकिन हैश ऑर्डर के आधार पर दूसरों के लिए विफल हो जाएगा।
  3. जैसा कि पहले ही उल्लेख किया गया है, इसके द्वारा लौटी अंतिम कुंजी को हटाना सुरक्षित है each। यह सही नहीं है keysक्योंकि सूची में रिटर्न eachकरते समय यह एक पुनरावृत्ति है keys

2
"कुंजी के लिए सच नहीं है", बल्कि: यह कुंजियों पर लागू नहीं है और कोई भी डिलीट सुरक्षित है। जिन वाक्यांशों का आप उपयोग करते हैं उनका तात्पर्य है कि कुंजी का उपयोग करते समय कुछ भी हटाना कभी भी सुरक्षित नहीं है।
ysth

2
पुन: "हैश पुनरावृत्तियों में से किसी के बारे में कुछ भी असुरक्षित नहीं है", अन्य खतरा यह मान रहा है कि प्रत्येक लूप शुरू करने से पहले इट्रेटर शुरुआत में है, जैसा कि अन्य उल्लेख करते हैं।
ysth

3

मैं हमेशा विधि 2 का उपयोग करता हूं। प्रत्येक का उपयोग करने का एकमात्र लाभ यह है कि यदि आप हैश प्रविष्टि के मूल्य (केवल पुन: असाइन करने के बजाय) पढ़ रहे हैं, तो आप लगातार हैश का संदर्भ नहीं दे रहे हैं।


3

हो सकता है कि मैं इस से कट जाऊं लेकिन मुझे लगता है कि यह व्यक्तिगत प्राथमिकता है। मैं डॉक्स में प्रत्येक () कुंजियों () या मानों () के अलावा अलग होने का कोई संदर्भ नहीं पा सकता (स्पष्ट के अलावा) वे "अलग-अलग चीजें वापस करते हैं" उत्तर। वास्तव में डॉक्स एक ही पुनरावृत्त का उपयोग करते हैं और वे सभी। उनकी प्रतियों के बजाय वास्तविक सूची मान लौटाएं, और किसी भी कॉल का उपयोग करके उस पर पुनरावृति करते हुए हैश को संशोधित करना बुरा है।

सभी ने कहा, मैं लगभग हमेशा कुंजियों का उपयोग करता हूं () क्योंकि मेरे लिए यह आमतौर पर हैश के माध्यम से कुंजी के मूल्य तक पहुंचने के लिए अधिक स्व दस्तावेज है। मैं कभी-कभी मूल्यों () का उपयोग करता हूं जब मूल्य एक बड़ी संरचना का संदर्भ होता है और हैश की कुंजी पहले से ही संरचना में संग्रहीत होती है, जिस बिंदु पर कुंजी अतिरेक है और मुझे इसकी आवश्यकता नहीं है। मुझे लगता है कि मैंने 10 साल के पर्ल प्रोग्रामिंग में प्रत्येक (2 बार) का उपयोग किया है और यह शायद दोनों बार गलत विकल्प था =)


2

मैं आमतौर पर उपयोग करता keysहूं और मैं पिछली बार के बारे में नहीं सोच सकता था जिसका मैंने उपयोग किया या पढ़ा each

mapआप पाश में क्या कर रहे हैं, इसके आधार पर मत भूलना !

map { print "$_ => $hash{$_}\n" } keys %hash;

6
जब तक आप वापसी मूल्य नहीं चाहते तब तक मानचित्र का उपयोग न करें
ko-dos

-1

मैं कहता हूँ:

  1. अधिकांश लोगों के लिए पढ़ने / समझने में जो कुछ भी सबसे आसान है उसका उपयोग करें (इसलिए आमतौर पर, मैं तर्क देता हूं)
  2. जो भी आप लगातार तय करते हैं उसका उपयोग पूरे कोड आधार से करें।

इससे 2 प्रमुख लाभ मिलते हैं:

  1. "सामान्य" कोड को स्पॉट करना आसान है ताकि आप फ़ंक्शंस / मेथियोड में फिर से फ़ैक्टर कर सकें।
  2. भविष्य के डेवलपर्स के लिए इसे बनाए रखना आसान है।

मुझे नहीं लगता कि प्रत्येक पर कुंजियों का उपयोग करना अधिक महंगा है, इसलिए आपके कोड में एक ही चीज़ के लिए दो अलग-अलग निर्माणों की आवश्यकता नहीं है।


1
साथ keysद्वारा स्मृति के उपयोग बढ़ जाती है hash-size * avg-key-size। यह देखते हुए कि कुंजी का आकार केवल मेमोरी तक सीमित है (क्योंकि वे हुड के नीचे "उनके" इसी तरह के मूल्यों जैसे सरणी तत्व हैं), कुछ स्थितियों में यह प्रतिलिपि बनाने के लिए स्मृति उपयोग और समय दोनों में निषेधात्मक रूप से अधिक महंगा हो सकता है।
एड्रियन गुंटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.