रूबी में सूची की समझ


93

पायथन सूची समझ के बराबर करने के लिए, मैं निम्नलिखित कर रहा हूं:

some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}

क्या ऐसा करने का एक बेहतर तरीका है ... शायद एक विधि कॉल के साथ?


3
आपके और ग्लेन mcdonald के दोनों उत्तर मुझे ठीक लगते हैं ... मैं यह नहीं देखता कि आप और अधिक संक्षिप्त होने की कोशिश करके क्या हासिल करेंगे।
पिस्टनोस

1
यह समाधान सूची को दो बार स्थानांतरित करता है। इंजेक्शन नहीं है।
पेड्रो रोलो

2
यहाँ कुछ भयानक जवाब है, लेकिन यह भी कई संग्रह में सूची समझ के लिए विचारों को देखने के लिए भयानक होगा।
बो जीन्स

जवाबों:


55

यदि आप वास्तव में चाहते हैं, तो आप इस तरह एक Array # comprehend विधि बना सकते हैं:

class Array
  def comprehend(&block)
    return self if block.nil?
    self.collect(&block).compact
  end
end

some_array = [1, 2, 3, 4, 5, 6]
new_array = some_array.comprehend {|x| x * 3 if x % 2 == 0}
puts new_array

प्रिंटों:

6
12
18

मैं शायद वैसे ही करूँगा जैसे आपने किया था।


2
आप कॉम्पैक्ट का उपयोग कर सकते हैं! थोड़ा सा अनुकूलन करने के लिए
एलेक्सी

9
यह वास्तव में सही नहीं है, विचार करें: [nil, nil, nil].comprehend {|x| x }जो रिटर्न देता है []
thirtyseven

एलेक्स, डॉक्स के अनुसार, compact!जब कोई आइटम नहीं बदला जाता है, तो सरणी के बजाय शून्य लौटाता है, इसलिए मुझे नहीं लगता कि यह काम करता है।
बाइनरी फाइल

89

कैसे रहा मुक्केबाज़ी:

some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact

थोड़ा क्लीनर, कम से कम मेरे स्वाद के लिए, और अपने संस्करण की तुलना में लगभग 15% तेज बेंचमार्क टेस्ट के अनुसार ...


4
साथ ही some_array.map{|x| x * 3 unless x % 2}.compact, जो यकीनन अधिक पठनीय / रूबी-एस्क है।
नाइटपूल

5
@ नाइटपूल unless x%2का कोई प्रभाव नहीं है क्योंकि 0 माणिक में सत्य है। देखें: gist.github.com/jfarmer/2647362
अभिनव श्रीवास्तव

30

मैंने तीन विकल्पों की तुलना में एक त्वरित बेंचमार्क बनाया और मानचित्र-कॉम्पैक्ट वास्तव में सबसे अच्छा विकल्प लगता है।

प्रदर्शन परीक्षण (रेल)

require 'test_helper'
require 'performance_test_help'

class ListComprehensionTest < ActionController::PerformanceTest

  TEST_ARRAY = (1..100).to_a

  def test_map_compact
    1000.times do
      TEST_ARRAY.map{|x| x % 2 == 0 ? x * 3 : nil}.compact
    end
  end

  def test_select_map
    1000.times do
      TEST_ARRAY.select{|x| x % 2 == 0 }.map{|x| x * 3}
    end
  end

  def test_inject
    1000.times do
      TEST_ARRAY.inject([]) {|all, x| all << x*3 if x % 2 == 0; all }
    end
  end

end

परिणाम

/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/performance/list_comprehension_test.rb" -- --benchmark
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
ListComprehensionTest#test_inject (1230 ms warmup)
           wall_time: 1221 ms
              memory: 0.00 KB
             objects: 0
             gc_runs: 0
             gc_time: 0 ms
.ListComprehensionTest#test_map_compact (860 ms warmup)
           wall_time: 855 ms
              memory: 0.00 KB
             objects: 0
             gc_runs: 0
             gc_time: 0 ms
.ListComprehensionTest#test_select_map (961 ms warmup)
           wall_time: 955 ms
              memory: 0.00 KB
             objects: 0
             gc_runs: 0
             gc_time: 0 ms
.
Finished in 66.683039 seconds.

15 tests, 0 assertions, 0 failures, 0 errors

1
reduceइस बेंचमार्क को देखना दिलचस्प होगा (देखें stackoverflow.com/a/17703276 )।
एडम लिंडबर्ग

3
inject==reduce
बेन.नैप्स

map_compact शायद अधिक तेज़ हो, लेकिन यह एक नया सरणी बना रहा है। इंजेक्षन map.compact और select.map से अंतरिक्ष कुशल है
bibstha

11

इस थ्रेड में रूबी प्रोग्रामर्स के बीच कुछ भ्रम की स्थिति है, जो सूची की समझ है। हर एक प्रतिक्रिया को बदलने के लिए कुछ आकर्षक सरणी होती है। लेकिन सूची समझने की शक्ति निम्नलिखित वाक्य रचना के साथ मक्खी पर बनाई गई एक सरणी में निहित है:

squares = [x**2 for x in range(10)]

रूबी में एक एनालॉग होगा (इस धागे में एकमात्र पर्याप्त उत्तर, AFAIC):

a = Array.new(4).map{rand(2**49..2**50)} 

उपरोक्त मामले में, मैं यादृच्छिक पूर्णांकों की एक सरणी बना रहा हूं, लेकिन ब्लॉक में कुछ भी हो सकता है। लेकिन यह एक रूबी सूची की समझ होगी।


1
ओपी क्या करने की कोशिश कर रहा है आप कैसे करेंगे?
एंड्रयू ग्रिम

2
वास्तव में अब मैं देख रहा हूँ कि ओपी खुद कुछ मौजूदा सूची में था जिसे लेखक बदलना चाहता था। लेकिन सूची बोध की कट्टर अवधारणा में एक सरणी / सूची बनाना शामिल है जहां कोई कुछ पुनरावृत्ति को संदर्भित करने से पहले मौजूद नहीं था। लेकिन वास्तव में, कुछ औपचारिक परिभाषाएं कहती हैं कि सूची समझ बिल्कुल भी मानचित्र का उपयोग नहीं कर सकती है, इसलिए यहां तक ​​कि मेरा संस्करण कोषेर नहीं है, लेकिन जितना मैं रूबी में अनुमान लगा सकता हूं।
मार्क

5
मुझे नहीं लगता कि आपके रूबी उदाहरण को आपके पायथन उदाहरण का एनालॉग माना जाता है। रूबी कोड को पढ़ना चाहिए: वर्ग = (0..9) .map {| x | x ** 2}
michau

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

4
अरे हाँ, यह (रूबी 2.0 के बाद से): squares_of_all_natural_numbers = (0.lolo :: INFINITY) .lazy.map {| x | एक्स ** 2}; p squares_of_all_natural_numbers.take (10) .to_a
मिचौ

11

मैंने इस विषय पर रेइन हेनरिक्स के साथ चर्चा की, जो मुझे बताते हैं कि सबसे अच्छा प्रदर्शन समाधान है

map { ... }.compact

यह अच्छा समझ में आता है क्योंकि यह मध्यवर्ती उपयोग के निर्माण से बचता है जैसा कि इसके अपरिवर्तनीय उपयोग के साथ होता है Enumerable#inject, और यह ऐरे को बढ़ने से रोकता है, जो आवंटन का कारण बनता है। जब तक आपके संग्रह में शून्य तत्व नहीं हो सकते, यह किसी भी अन्य के समान सामान्य है।

मैंने इसके साथ तुलना नहीं की है

select {...}.map{...}

यह संभव है कि रूबी का सी कार्यान्वयन Enumerable#selectबहुत अच्छा है।


9

एक वैकल्पिक समाधान जो हर कार्यान्वयन में काम करेगा और O (2n) समय के बजाय O (n) में चलेगा:

some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}

11
आपका मतलब है कि यह सूची को केवल एक बार ट्रेस करता है। यदि आप औपचारिक परिभाषा के अनुसार जाते हैं, तो O (n) O (2n) के बराबर है। बस नाइटपिंगिंग :)
डैनियल हेपर

1
@ डैनियल हार्पर :) न केवल आप सही हैं, बल्कि औसत मामले के लिए भी, कुछ प्रविष्टियों को छोड़ने के लिए एक बार सूची को स्थानांतरित करना, और फिर एक ऑपरेशन करने के लिए औसत मामलों में एकांत बेहतर हो सकता है :)
पेड्रो रोलो

दूसरे शब्दों में, आप क्या कर रहे हैं 2चीजों nके बजाय बार 1बात nबार और फिर एक और 1बात यह है कि nकई बार :) में से एक महत्वपूर्ण लाभ inject/ reduceकि यह किसी भी बरकरार रखता है है nilइनपुट अनुक्रम जो और अधिक सूची-comprehensionly व्यवहार है में मान
जॉन ला Rooy

8

मैंने अभी-अभी रूबी जीम्स को कॉम्प्रिहेंड रत्न प्रकाशित किया है , जो आपको ऐसा करने देता है:

require 'comprehend'

some_array.comprehend{ |x| x * 3 if x % 2 == 0 }

यह C में लिखा है; सरणी केवल एक बार ट्रेस की गई है।


7

Enumerable की एक grepविधि है जिसका पहला तर्क एक विधेय खरीद हो सकता है, और जिसका वैकल्पिक दूसरा तर्क एक मैपिंग फ़ंक्शन है; तो निम्नलिखित काम करता है:

some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}

यह अन्य सुझावों के एक जोड़े के रूप में पठनीय नहीं है (मुझे एनोइक के सरल select.mapया हिस्टोक्रेट के समझदार मणि पसंद है), लेकिन इसकी ताकत यह है कि यह पहले से ही मानक पुस्तकालय का हिस्सा है, और एकल-पास है और इसमें अस्थायी मध्यवर्ती सरणियों को शामिल नहीं किया गया है , और इनयूज़िंग सुझावों nilमें इस्तेमाल किए गए आउट-ऑफ-बाउंड्स मूल्य की आवश्यकता नहीं है compact


4

यह अधिक संक्षिप्त है:

[1,2,3,4,5,6].select(&:even?).map{|x| x*3}

2
या, और भी अधिक बिंदु-मुक्त अशुद्धि के लिए[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
Jörg W Mittag

4
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact
=> [6, 12, 18]

ये मेरे लिए सही है। यह साफ भी है। हां, यह वैसा ही है map, लेकिन मुझे लगता है collectकि यह कोड को अधिक समझने योग्य बनाता है।


select(&:even?).map()

वास्तव में बेहतर लग रहा है, नीचे देखने के बाद।


2

जैसा पेड्रो उल्लेख किया है, आप के लिए श्रृंखलित कॉल एक साथ फ्यूज कर सकते हैं Enumerable#selectऔर Enumerable#map, चयनित तत्वों पर एक ट्रेवर्सल से परहेज। यह सच है क्योंकि Enumerable#selectतह की विशेषज्ञता है या inject। मैंने जल्दबाजी में परिचय पोस्ट किया रूबी सब्रेडिट में विषय पर दिया।

मैन्युअल रूप से फ़्यूज़िंग एरे ट्रांसफ़ॉर्मेशन थकाऊ हो सकता है, इसलिए हो सकता है कि कोई व्यक्ति रॉबर्ट गैम्बल के comprehendकार्यान्वयन के साथ इसे select/ mapपैटर्न को पूर्ववर्ती बना सकता है।


2

कुछ इस तरह:

def lazy(collection, &blk)
   collection.map{|x| blk.call(x)}.compact
end

इसे कहते हैं:

lazy (1..6){|x| x * 3 if x.even?}

कौन सा रिटर्न:

=> [6, 12, 18]

lazyएरे पर और फिर परिभाषित करने में क्या गलत है :(1..6).lazy{|x|x*3 if x.even?}
गस

1

एक और समाधान लेकिन शायद सबसे अच्छा नहीं

some_array.flat_map {|x| x % 2 == 0 ? [x * 3] : [] }

या

some_array.each_with_object([]) {|x, list| x % 2 == 0 ? list.push(x * 3) : nil }

0

यह दृष्टिकोण करने का एक तरीका है:

c = -> x do $*.clear             
  if x['if'] && x[0] != 'f' .  
    y = x[0...x.index('for')]    
    x = x[x.index('for')..-1]
    (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}")
    x.insert(x.length, "end; $*")
    eval(x)
    $*)
  elsif x['if'] && x[0] == 'f'
    (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << x")
    x.insert(x.length, "end; $*")
    eval(x)
    $*)
  elsif !x['if'] && x[0] != 'f'
    y = x[0...x.index('for')]
    x = x[x.index('for')..-1]
    (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}")
    x.insert(x.length, "end; $*")
    eval(x)
    $*)
  else
    eval(x.split[3]).to_a
  end
end 

मूल रूप से हम एक स्ट्रिंग को लूप के लिए उचित रूबी सिंटैक्स में परिवर्तित कर रहे हैं तब हम करने के लिए एक स्ट्रिंग में अजगर सिंटैक्स का उपयोग कर सकते हैं:

c['for x in 1..10']
c['for x in 1..10 if x.even?']
c['x**2 for x in 1..10 if x.even?']
c['x**2 for x in 1..10']

# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# [2, 4, 6, 8, 10]
# [4, 16, 36, 64, 100]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

या अगर आपको स्ट्रिंग दिखती है या लंबोदर का उपयोग करने का तरीका पसंद नहीं है, तो हम अजगर सिंटैक्स को दर्पण करने का प्रयास कर सकते हैं और ऐसा कुछ कर सकते हैं:

S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1]
# [0, 4, 8, 12, 16]

0

रूबी 2.7 पेश किया, filter_mapजो बहुत कुछ हासिल करता है जो आप चाहते हैं (मानचित्र + कॉम्पैक्ट):

some_array.filter_map { |x| x * 3 if x % 2 == 0 }

आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं ।



-4

मुझे लगता है कि सबसे अधिक सूची समझ-एस्क निम्नलिखित होगा:

some_array.select{ |x| x * 3 if x % 2 == 0 }

चूंकि रूबी हमें अभिव्यक्ति के बाद सशर्त जगह देने की अनुमति देती है, हमें सूची समझ के पायथन संस्करण के समान वाक्यविन्यास मिलता है। इसके अलावा, चूंकि selectविधि में ऐसा कुछ भी शामिल नहीं है जो इसके बराबर है false, सभी शून्य मान परिणामी सूची से हटा दिए जाते हैं और कॉम्पैक्ट पर कोई कॉल करना आवश्यक नहीं है, जैसा कि हमने इस्तेमाल किया था mapया collectइसके बजाय यह होगा।


7
यह काम नहीं करता है। कम से कम रूबी 1.8.6 में, [1,2,3,4,5,6] .select {| x | x * 3 यदि x% 2 == 0} [2, 4, 6] का मूल्यांकन करता है, तो एन्यूमरेबल # चयन केवल इस बात की परवाह करता है कि ब्लॉक सही या गलत का मूल्यांकन करता है, न कि यह मान क्या है, AFAIK।
ग्रेग कैंपबेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.