2008-10-01 45 views
13

मैं प्रविष्टियों जिस पर मैं कुछ सरल आंकड़े एकत्र करना चाहते का एक iterable है में मिलान तत्वों की गिनती के अधिकांश pythonic तरीका, दो से विभाज्य सभी नंबरों की गिनती और तीन से विभाज्य सभी नंबरों की गिनती का कहना है।कुछ iterable

(आल्ट 1)

r = xrange(1, 10) 

twos = 0 
threes = 0 

for v in r: 
    if v % 2 == 0: 
    twos+=1 
    if v % 3 == 0: 
    threes+=1 

print twos 
print threes 

यह:

मेरा पहला विकल्प, केवल एक बार सूची के माध्यम से पुनरावृत्ति और सूची में विस्तार से परहेज (और मन में split loop रिफैक्टरिंग रखने) है, बल्कि फूला हुआ लग रहा है बल्कि अच्छा लग रहा है, लेकिन एक सूची में अभिव्यक्ति के विस्तार की सबसे बड़ी खामी है:

(आल्ट 2)

r = xrange(1, 10) 

print len([1 for v in r if v % 2 == 0]) 
print len([1 for v in r if v % 3 == 0]) 

क्या मैं सच में चाहते हैं इस तरह एक समारोह की तरह कुछ है:

(आल्ट 3)

def count(iterable): 
    n = 0 
    for i in iterable: 
    n += 1 
    return n 

r = xrange(1, 10) 

print count(1 for v in r if v % 2 == 0) 
print count(1 for v in r if v % 3 == 0) 

लेकिन यह कुछ ऐसा है जो एक समारोह के बिना किया जा सकता है की तरह एक बहुत लग रहा है।

(आल्ट 4)

r = xrange(1, 10) 

print sum(1 for v in r if v % 2 == 0) 
print sum(1 for v in r if v % 3 == 0) 

और जब तक छोटी (और मेरी किताब में शायद सबसे खूबसूरत) यह महसूस नहीं करता है यह बहुत अच्छी तरह से आशय व्यक्त करता है जैसे: अंतिम संस्करण यह है।

तो, आप करने के लिए अपने सवाल यह है:

कौन सा विकल्प आप आँकड़े के इन प्रकार के इकट्ठा करने के लिए सबसे ज्यादा पसंद करते हैं? अगर आप कुछ बेहतर है अपने खुद के विकल्प की आपूर्ति के लिए स्वतंत्र महसूस।

  • हकीकत में मेरी फिल्टर विधेय सिर्फ इस साधारण परीक्षण की तुलना में अधिक जटिल हैं:

    नीचे कुछ भ्रम की स्थिति स्पष्ट करने के लिए।

  • वस्तुओं मैं पुनरावृति
  • मेरे फिल्टर कार्यों अधिक अलग और एक विधेय

उत्तर

14

कई बार सूची में पुनरावृत्ति करने के लिए सुरुचिपूर्ण IMHO नहीं है।

मैं शायद एक समारोह की अनुमति देता है कि कर बनाएंगे:

twos, threes = countmatching(xrange(1,10), 
          lambda a: a % 2 == 0, 
          lambda a: a % 3 == 0) 

एक प्रारंभिक बिंदु कुछ इस तरह होगा:

def countmatching(iterable, *predicates): 
    v = [0] * len(predicates) 
    for e in iterable: 
     for i,p in enumerate(predicates): 
      if p(e): 
       v[i] += 1 
    return tuple(v) 

Btw, "itertools व्यंजनों" बहुत करने के लिए एक नुस्खा है अपनी alt4 की तरह।

def quantify(seq, pred=None): 
    "Count how many times the predicate is true in the sequence" 
    return sum(imap(pred, seq)) 
+0

पुन:। दो बार पुनरावृत्त करना, इसमें कुछ चीजें स्पष्टता के लिए जा रही हैं, लेकिन इसके अलावा, गैर-सी-कोड निष्पादित की मात्रा द्वारा एक बार फिर से शुरू होने से कम ओवरहेड नहीं होगा? –

+0

बेशक, अगर मैं केवल पुनरावृति एक बार यह एक शॉट पर काम करता है भी iterables, ओह :) है कि अब तक लगता है कि नहीं था। –

+0

मुझे आपका समाधान पसंद है। लेकिन आप कहते हैं: "कई बार सूची में पुनरावृत्ति करना सुरुचिपूर्ण नहीं है"। यदि एन मान हैं और एम भविष्यवाणी करता है, तो आप एम पर अनुमान लगाते हैं, तो एन मानों पर एम बार को फिर से करने का क्या फायदा है? नोट: मैं एक पाइथन नौसिखिया हूँ। चीयर्स! –

3

आप filter समारोह में इस्तेमाल कर सकते हैं parameterize करने के लिए कड़ी मेहनत कर रहे हैं बड़ा है और सिर्फ संख्या की तुलना में अधिक जटिल हैं।

यह एक सूची (या कड़ाई से एक iterable) केवल आइटम जो के लिए निर्दिष्ट समारोह सही का आकलन युक्त एक नई सूची का निर्माण फिल्टर।

r = xrange(1, 10) 

def is_div_two(n): 
    return n % 2 == 0 

def is_div_three(n): 
    return n % 3 == 0 

print len(filter(is_div_two,r)) 
print len(filter(is_div_three,r)) 

यह अच्छा है के रूप में यह आपको अपनी आंकड़ा तर्क एक समारोह में निहित और filter की मंशा बहुत स्पष्ट होना चाहिए रखने की अनुमति देता है।

+0

दूसरा प्रिंट एक को इस्तेमाल किया इटरेटर लेने वाली नहीं किया जाएगा और इसलिए 0 प्रिंट होगा? –

0

मैं निश्चित रूप से एक iterable सूची अगर आप सिर्फ नंबर हैं के बजाय एक numpy सरणी पर विचार करना होगा। आप लगभग निश्चित रूप से सरणी पर कुछ संक्षिप्त गणित के साथ आप क्या चाहते हैं ऐसा करने में सक्षम हो जाएगा।

+0

दुर्भाग्यवश यह वास्तव में बड़ी वस्तुओं की एक लंबी पुनरावृत्ति है; संख्या चीजें सिर्फ पढ़ने के लिए हैं :) –

1

अच्छी तरह से आप उस सूची परीक्षण के साथ tuples का एक सेट प्राप्त करने के लिए एक सूची समझ/अभिव्यक्ति कर सकते हैं और फिर रकम प्राप्त करने के लिए इसे कम कर सकते हैं।


r=xrange(10) 
s=((v % 2 == 0, v % 3 == 0) for v in r) 
def add_tuples(t1,t2): 
    return tuple(x+y for x,y in zip(t1, t2)) 
sums=reduce(add_tuples, s, (0,0)) # (0,0) is starting amount 

print sums[0] # sum of numbers divisible by 2 
print sums[1] # sum of numbers divisible by 3 
 

जनरेटर अभिव्यक्ति आदि का उपयोग करना मतलब है कि आप केवल एक बार iterator के माध्यम से चलाने जाएगा चाहिए (जब तक कम करने के लिए कुछ भी अजीब है?)। असल में आप नक्शा/कम कर रहे होंगे ...

+0

हां! मुझे पता था कि इसके लिए कम करने का एक तरीका था :) –

0

जैसा कि आप ढूंढ रहे हैं उतना ही उतना ही कुशल नहीं है, लेकिन यह अधिक कुशल है, यह वास्तव में किसी भी प्रकार के साथ काम करता है, न कि केवल बार-बार आप कई बार लूप कर सकते हैं, और आप विस्तार कर सकते हैं चीजों को आगे जटिल किए बिना जांचने के लिए:

r = xrange(1, 10) 

counts = { 
    2: 0, 
    3: 0, 
} 

for v in r: 
    for q in counts: 
     if not v % q: 
      counts[q] += 1 
     # Or, more obscure: 
     #counts[q] += not v % q 

for q in counts: 
    print "%s's: %s" % (q, counts[q]) 
6

Alt 4! लेकिन हो सकता है कि आपको कोड को ऐसे फ़ंक्शन पर दोबारा दोहराया जाए जो तर्क लेता है जिसमें विभाजित संख्या (दो और तीन) होनी चाहिए। और फिर आप एक बेहतर समारोह नाम हो सकता है।

def methodName(divNumber, r): 
    return sum(1 for v in r if v % divNumber == 0) 


print methodName(2, xrange(1, 10)) 
print methodName(3, xrange(1, 10)) 
+0

'वास्तविक' परीक्षण दुर्भाग्यवश उससे थोड़ा अलग हैं। उन्हें पैरामीटर करने से मुझे केवल सिरदर्द मिलेगा :) –

0
from itertools import groupby 
from collections import defaultdict 

def multiples(v): 
    return 2 if v%2==0 else 3 if v%3==0 else None 
d = defaultdict(list) 

for k, values in groupby(range(10), multiples): 
    if k is not None: 
     d[k].extend(values) 
+0

कूल समाधान, हालांकि आंकड़े सही ढंग से अपडेट नहीं होते हैं जब कोई आइटम दो और तीन दोनों द्वारा विभाजित होता है। की –

0

विचार यहाँ कमी उपयोग करने के लिए बार-बार पुनरावृत्तियों से बचना है। इसके अलावा, यदि स्मृति आपके लिए कोई समस्या है, तो यह कोई अतिरिक्त डेटा संरचना नहीं बनाता है। आप अपने काउंटर ({'div2': 0, 'div3': 0}) के साथ एक शब्दकोश के साथ शुरू करते हैं और पुनरावृत्ति के साथ उन्हें बढ़ाते हैं।

def increment_stats(stats, n): 
    if n % 2 == 0: stats['div2'] += 1 
    if n % 3 == 0: stats['div3'] += 1 
    return stats 

r = xrange(1, 10) 
stats = reduce(increment_stats, r, {'div2': 0, 'div3': 0}) 
print stats 

आप कुछ भी divisors तुलना में अधिक जटिल गणना करना चाहते हैं, तो यह एक अधिक वस्तु उन्मुख दृष्टिकोण (एक ही लाभ के साथ) का उपयोग करने के उपयुक्त होगा, आँकड़े निकासी के लिए तर्क encapsulating।

class Stats: 

    def __init__(self, div2=0, div3=0): 
     self.div2 = div2 
     self.div3 = div3 

    def increment(self, n): 
     if n % 2 == 0: self.div2 += 1 
     if n % 3 == 0: self.div3 += 1 
     return self 

    def __repr__(self): 
     return 'Stats(%d, %d)' % (self.div2, self.div3) 

r = xrange(1, 10) 
stats = reduce(lambda stats, n: stats.increment(n), r, Stats()) 
print stats 

कृपया कोई गलती बताएं।

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

+0

अजीब और पेचीदा उपयोग को कम :) और हाँ, और अधिक जटिल परिदृश्यों के लिए एक से थोड़ा अधिक OO दृष्टिकोण को प्राथमिकता दी जाएगी, लेकिन मैं काफी देख नहीं कैसे अपने संस्करण बेहतर मापता है (रखरखाव/पुन: उपयोग के लिहाज से) मूल की तुलना में । –

0

ऊपर OO-वार से प्रेरित होकर, मैं भी एक पर मेरे हाथ की कोशिश करने का है (हालांकि इस समस्या को हल करने के लिए मैं :)

class Stat(object): 
    def update(self, n): 
    raise NotImplementedError 

    def get(self): 
    raise NotImplementedError 


class TwoStat(Stat): 
    def __init__(self): 
    self._twos = 0 

    def update(self, n): 
    if n % 2 == 0: self._twos += 1 

    def get(self): 
    return self._twos 


class ThreeStat(Stat): 
    def __init__(self): 
    self._threes = 0 

    def update(self, n): 
    if n % 3 == 0: self._threes += 1 

    def get(self): 
    return self._threes 


class StatCalculator(object): 
    def __init__(self, stats): 
    self._stats = stats 

    def calculate(self, r): 
    for v in r: 
     for stat in self._stats: 
     stat.update(v) 
    return tuple(stat.get() for stat in self._stats) 


s = StatCalculator([TwoStat(), ThreeStat()]) 

r = xrange(1, 10) 
print s.calculate(r) 
1

यह सच है कोशिश कर रहा हूँ के लिए रास्ता overkill है था बूलियन यूनिट पूर्णांक के लिए मजबूर होते हैं, और झूठे बूलियन शून्य पूर्णांक तक होते हैं। तो यदि आप scipy या numpy का उपयोग करने में प्रसन्न हैं, तो अपने अनुक्रम के प्रत्येक तत्व के लिए पूर्णांक की सरणी बनाएं, प्रत्येक सरणी में आपके प्रत्येक परीक्षण के लिए एक तत्व होता है, और सरणी पर योग होता है। जैसे

>>> sum(scipy.array([c % 2 == 0, c % 3 == 0]) for c in xrange(10)) 
array([5, 4]) 
0

Alt 3, इस कारण से कि यह "हिट" की संख्या के आनुपातिक स्मृति का उपयोग नहीं करता है। Xrange (one_trillion) जैसे पैथोलॉजिकल केस को देखते हुए, कई अन्य प्रस्तावित समाधान बुरी तरह विफल हो जाएंगे।

+0

मुझे लगता है कि alt 4 एक ही गुण –

2

मैं अपने (आल्ट 4) का एक छोटा सा संस्करण का चयन करेंगे:

def count(predicate, list): 
    print sum(1 for x in list if predicate(x)) 

r = xrange(1, 10) 

count(lambda x: x % 2 == 0, r) 
count(lambda x: x % 3 == 0, r) 
# ... 

आप बदलना क्या गिनती करता है, एक ही स्थान पर इसके कार्यान्वयन को बदलने चाहते हैं।

नोट: के बाद से अपने विधेय जटिल हैं, तो आप शायद उन्हें lambdas के बजाय कार्यों में परिभाषित करने के लिए चाहता हूँ। और इसलिए आप शायद इसे वैश्विक नामस्थान की बजाय कक्षा में रखना चाहते हैं।

+0

बदलने क्या गिनती करता है बहुत आम नहीं किया जाएगा है, लेकिन एक समारोह नामित गिनती बनाने एक अच्छा तरीका में आशय दिखा मदद करता है। पुन:। आपका नोट; निश्चित रूप से, लेकिन यह सवाल के दायरे से बाहर होगा :) –

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