एक पर्ल सरणी के माध्यम से पुनरावृति करने का सबसे अच्छा तरीका


94

पर्ल सरणी के माध्यम से पुनरावृत्ति के लिए सबसे अच्छा कार्यान्वयन (गति और स्मृति उपयोग के संदर्भ में) कौन सा है? क्या कोई बेहतर तरीका है? ( @Arrayजरूरत नहीं बनाए रखा जाना चाहिए)।

कार्यान्वयन १

foreach (@Array)
{
      SubRoutine($_);
}

क्रियान्वयन २

while($Element=shift(@Array))
{
      SubRoutine($Element);
}

क्रियान्वयन ३

while(scalar(@Array) !=0)
{
      $Element=shift(@Array);
      SubRoutine($Element);
}

क्रियान्वयन ४

for my $i (0 .. $#Array)
{
      SubRoutine($Array[$i]);
}

क्रियान्वयन ५

map { SubRoutine($_) } @Array ;

2
एक "सर्वश्रेष्ठ" क्यों होगा? विशेष रूप से यह देखते हुए कि हमें पता नहीं है कि आप एक को दूसरे के खिलाफ कैसे मापेंगे (स्मृति उपयोग की तुलना में गति अधिक महत्वपूर्ण है? mapऔर स्वीकार्य उत्तर? आदि)
मैक्स लिबर्ट रॉबर्ट

2
आपके द्वारा पोस्ट किए गए तीन में से दो मुझे "WTH!" जब तक कि उन्हें समझदार विकल्प बनाने के लिए अतिरिक्त आसपास के संदर्भ के रूप में न हो। किसी भी मामले में, यह सवाल " दो नंबर जोड़ने का सबसे अच्छा तरीका क्या है? " के स्तर पर है, ज्यादातर समय, केवल एक ही रास्ता है। फिर, वे परिस्थितियां हैं, जहां आपको अलग तरीके की जरूरत है। मतदान बंद
सिनान Maynür

4
@ Sinan opinionnür मैं आपकी राय के साथ सहानुभूति रखता हूं (कि दो संख्याओं को जोड़ने का केवल एक ही तरीका है), लेकिन उपमा पर्याप्त रूप से मजबूत नहीं है। जाहिर है, एक से अधिक तरीके हैं, और ओपी समझना चाहता है कि एक अच्छा विचार क्या है और क्या नहीं है।
CodeClown42

2
प्रोग्रामिंग पर्ल के तीसरे संस्करण के अध्याय 24 में दक्षता पर एक अनुभाग है जो एक अच्छा पढ़ा गया है। यह विभिन्न प्रकार की दक्षता जैसे समय, प्रोग्रामर, अनुरक्षक को संबोधित करता है। यह खंड "नोट के साथ शुरू होता है" ध्यान दें कि समय के लिए अनुकूलन कभी-कभी आपको अंतरिक्ष या प्रोग्रामर दक्षता (नीचे दिए गए परस्पर विरोधी संकेतों से संकेत मिलता है) में खर्च हो सकता है। उनका ब्रेक टूट जाता है। "

1
दो नंबर जोड़ने का एक 1 तरीका? यदि आप निचले स्तर की कॉल / क्रियान्वयन में नहीं दिखते हैं .... लगता है कि
लुकहेड

जवाबों:


76
  • गति के संदर्भ में: # 1 और # 4, लेकिन अधिकांश उदाहरणों में ज्यादा नहीं।

    आप पुष्टि करने के लिए एक बेंचमार्क लिख सकते हैं, लेकिन मुझे संदेह है कि आप # 1 और # 4 को थोड़ा तेज़ पाएंगे, क्योंकि इट्रिएशन का काम पर्ल के बजाय सी में किया जाता है, और सरणी तत्वों की कोई अनावश्यक नकल नहीं होती है। ( $_है एलियास # 1 में तत्व है, लेकिन # 2 और # 3 वास्तव में नकल सरणी से scalars।)

    # 5 समान हो सकता है।

  • स्मृति उपयोग के संदर्भ में: वे # 5 को छोड़कर सभी समान हैं।

    for (@a)सरणी को समतल करने से बचने के लिए विशेष आवरण है। लूप सरणी के अनुक्रमित पर पुनरावृत्त करता है।

  • पठनीयता के संदर्भ में: # 1।

  • लचीलेपन के संदर्भ में: # 1 / # 4 और # 5।

    # 2 ऐसे तत्वों का समर्थन नहीं करता जो झूठे हैं। # 2 और # 3 विनाशकारी हैं।


3
वाह, आपने छोटे और सरल वाक्यों में जानकारी के ट्रक लोड को जोड़ा।
जयपाल सिंह

1
# 2 अच्छा है जब आप कतारों में लग जाते हैं (जैसे चौड़ाई-पहली खोज):my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
ikegami

क्या कार्यान्वयन 4 सूचकांकों का एक मध्यवर्ती सरणी नहीं बनाता है, जिसका उपयोग करने के लिए स्मृति की एक बड़ी मात्रा का परिचय हो सकता है? यदि हां, तो लगता है कि एक को उस दृष्टिकोण का उपयोग नहीं करना चाहिए। stackoverflow.com/questions/6440723/... rt.cpan.org/Public/Bug/Display.html?id=115863
Thorsten Schöning

@ikegami आपकी चैंपियन शैली के लिए सही है - शानदार उत्तर :)
स्केलेस्टैक्स

26

यदि आप केवल तत्वों के बारे में परवाह करते हैं @Array, तो उपयोग करें:

for my $el (@Array) {
# ...
}

या

यदि इंडेक्स मायने रखता है, तो उपयोग करें:

for my $i (0 .. $#Array) {
# ...
}

या, perl5.12.1 के रूप में , आप उपयोग कर सकते हैं:

while (my ($i, $el) = each @Array) {
# ...
}

यदि आपको लूप के शरीर में तत्व और उसके सूचकांक दोनों की आवश्यकता है, मुझे अपेक्षा होगी का उपयोग करते हुए each सबसे तेज़, लेकिन फिरआप पूर्व 5.12.1 perls के साथ संगतता छोड़ देंगे ।

इनकी तुलना में कुछ अन्य पैटर्न कुछ परिस्थितियों में उपयुक्त हो सकते हैं।


मैं eachसबसे धीमी होने की उम्मीद करूंगा । यह दूसरों के सभी काम करता है एक उपनाम, प्लस एक सूची असाइनमेंट, दो स्केलर प्रतियां और दो स्केलर समाशोधन।
इकेगामी

और, मेरी सबसे अच्छी माप क्षमता के लिए, आप सही हैं। forएक सरणी के सूचकांकों पर पुनरावृत्ति के साथ लगभग 45% तेजी से , और एक सरणी संदर्भ के सूचकांकों पर पुनरावृत्ति करते समय 20% तेजी से (मैं $array->[$i]शरीर में पहुंच ), eachके साथ संयोजन के रूप में उपयोग करता हूं while
सिनान Maynür

3

IMO, कार्यान्वयन # 1 विशिष्ट है और पर्ल के लिए छोटा और मुहावरेदार होने के कारण दूसरों के लिए अकेले ही ट्रम्प करता है। तीन विकल्पों का एक बेंचमार्क आपको गति में अंतर्दृष्टि प्रदान कर सकता है, कम से कम।


2

1 2 और 3 से काफी भिन्न होता है, क्योंकि यह सरणी को चातुर्य में छोड़ देता है, जबकि अन्य दो इसे खाली छोड़ देते हैं।

मैं कहूंगा कि # 3 बहुत निराला है और शायद कम कुशल है, इसलिए भूल जाते हैं।

जो आपको # 1 और # 2 के साथ छोड़ देता है, और वे एक ही काम नहीं करते हैं, इसलिए एक दूसरे से "बेहतर" नहीं हो सकता है। यदि सरणी बड़ी है और आपको इसे रखने की आवश्यकता नहीं है, तो आम तौर पर गुंजाइश इससे निपटेगी ( लेकिन नोट देखें ), इसलिए आमतौर पर , # 1 अभी भी सबसे स्पष्ट और सरल विधि है। प्रत्येक तत्व को बंद करने से कुछ भी गति नहीं होगी। यहां तक ​​कि अगर सरणी को संदर्भ से मुक्त करने की आवश्यकता है, तो मैं अभी जाऊंगा:

undef @Array;

जब हो जाए।

  • ध्यान दें : सरणी के दायरे वाले सबरूटीन वास्तव में सरणी रखता है और अगली बार अंतरिक्ष का फिर से उपयोग करता है। आम तौर पर , यह ठीक होना चाहिए (टिप्पणियों को देखें)।

@Array = ();अंतर्निहित सरणी को मुक्त नहीं करता है। दायरे से बाहर जाने पर भी ऐसा नहीं होता। यदि आप अंतर्निहित सरणी को मुक्त करना चाहते हैं, तो आप उपयोग करेंगे undef @Array;
इकेगामी

2
डेमो; perl -MDevel::Peek -e'my @a; Dump(\@a,1); @a=qw( a b c ); Dump(\@a,1); @a=(); Dump(\@a,1); undef @a; Dump(\@a,1);' 2>&1 | grep ARRAY
इकेगामी

क्या??? मैंने सोचा था कि जीसी का पूरा बिंदु एक बार रेफरी == 0 था, इसमें शामिल मेमोरी रिसाइकिल हो जाती है।
CodeClown42

@ikegami: मैं ()बनाम के बारे में बात देख रहा हूं undef, लेकिन अगर किसी दायरे से बाहर जाने वाली मेमोरी उस दायरे में उपयोग की गई मेमोरी को रिलीज़ नहीं करती है, तो क्या इससे पर्ल को कोई लीक नहीं होता? यह सच नहीं हो सकता।
CodeClown42

वे या तो लीक नहीं करते। उप अब भी उनका मालिक है, और अगली बार जब सब को बुलाया जाएगा तो उनका पुन: उपयोग करेंगे। गति के लिए अनुकूलित।
इकेगामी

1

तत्व या सरणी को मुद्रित करने के लिए एकल पंक्ति में।

$ (_array) के लिए $ _ प्रिंट करें;

नोट: याद रखें कि $ _ आंतरिक रूप से लूप में @ ऐरे के तत्व का उल्लेख कर रहा है। $ _ में किए गए कोई भी परिवर्तन @ श्रेणी में प्रतिबिंबित होंगे; पूर्व।

my @array = qw( 1 2 3 );
for (@array) {
        $_ = $_ *2 ;
}
print "@array";

आउटपुट: 2 4 6


0

उन्हें बेंचमार्क करने के लिए इस तरह से सवाल तय करने का सबसे अच्छा तरीका:

use strict;
use warnings;
use Benchmark qw(:all);

our @input_array = (0..1000);

my $a = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    foreach my $element (@array) {
       die unless $index == $element;
       $index++;
    }
};

my $b = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (defined(my $element = shift @array)) {
       die unless $index == $element;
       $index++;
    }
};

my $c = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (scalar(@array) !=0) {
       my $element = shift(@array);
       die unless $index == $element;
       $index++;
    }
};

my $d = sub {
    my @array = @{[ @input_array ]};
    foreach my $index (0.. $#array) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $e = sub {
    my @array = @{[ @input_array ]};
    for (my $index = 0; $index <= $#array; $index++) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $f = sub {
    my @array = @{[ @input_array ]};
    while (my ($index, $element) = each @array) {
       die unless $index == $element;
    }
};

my $count;
timethese($count, {
   '1' => $a,
   '2' => $b,
   '3' => $c,
   '4' => $d,
   '5' => $e,
   '6' => $f,
});

और इसे perl 5, संस्करण 24, तोड़फोड़ 1 (v5.24.1) पर x86_64-linux-gnu-thread-multi के लिए बनाया गया

मुझे मिला:

Benchmark: running 1, 2, 3, 4, 5, 6 for at least 3 CPU seconds...
         1:  3 wallclock secs ( 3.16 usr +  0.00 sys =  3.16 CPU) @ 12560.13/s (n=39690)
         2:  3 wallclock secs ( 3.18 usr +  0.00 sys =  3.18 CPU) @ 7828.30/s (n=24894)
         3:  3 wallclock secs ( 3.23 usr +  0.00 sys =  3.23 CPU) @ 6763.47/s (n=21846)
         4:  4 wallclock secs ( 3.15 usr +  0.00 sys =  3.15 CPU) @ 9596.83/s (n=30230)
         5:  4 wallclock secs ( 3.20 usr +  0.00 sys =  3.20 CPU) @ 6826.88/s (n=21846)
         6:  3 wallclock secs ( 3.12 usr +  0.00 sys =  3.12 CPU) @ 5653.53/s (n=17639)

तो 'foreach (@Array)' दूसरों की तुलना में दोगुना तेज़ है। अन्य सभी बहुत समान हैं।

@ ईकगामी यह भी बताता है कि गति के अलावा इन निहितार्थों में काफी अंतर हैं।


1
तुलना $index < $#arrayवास्तव में होनी चाहिए $index <= $#arrayक्योंकि $#arrayयह सरणी की लंबाई नहीं है बल्कि इसका अंतिम सूचकांक है।
josch
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.