पायथन सूची समझ के बराबर करने के लिए, मैं निम्नलिखित कर रहा हूं:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
क्या ऐसा करने का एक बेहतर तरीका है ... शायद एक विधि कॉल के साथ?
पायथन सूची समझ के बराबर करने के लिए, मैं निम्नलिखित कर रहा हूं:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
क्या ऐसा करने का एक बेहतर तरीका है ... शायद एक विधि कॉल के साथ?
जवाबों:
यदि आप वास्तव में चाहते हैं, तो आप इस तरह एक 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
मैं शायद वैसे ही करूँगा जैसे आपने किया था।
[nil, nil, nil].comprehend {|x| x }जो रिटर्न देता है []।
compact!जब कोई आइटम नहीं बदला जाता है, तो सरणी के बजाय शून्य लौटाता है, इसलिए मुझे नहीं लगता कि यह काम करता है।
कैसे रहा मुक्केबाज़ी:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
थोड़ा क्लीनर, कम से कम मेरे स्वाद के लिए, और अपने संस्करण की तुलना में लगभग 15% तेज बेंचमार्क टेस्ट के अनुसार ...
some_array.map{|x| x * 3 unless x % 2}.compact, जो यकीनन अधिक पठनीय / रूबी-एस्क है।
unless x%2का कोई प्रभाव नहीं है क्योंकि 0 माणिक में सत्य है। देखें: gist.github.com/jfarmer/2647362
मैंने तीन विकल्पों की तुलना में एक त्वरित बेंचमार्क बनाया और मानचित्र-कॉम्पैक्ट वास्तव में सबसे अच्छा विकल्प लगता है।
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
reduceइस बेंचमार्क को देखना दिलचस्प होगा (देखें stackoverflow.com/a/17703276 )।
inject==reduce
इस थ्रेड में रूबी प्रोग्रामर्स के बीच कुछ भ्रम की स्थिति है, जो सूची की समझ है। हर एक प्रतिक्रिया को बदलने के लिए कुछ आकर्षक सरणी होती है। लेकिन सूची समझने की शक्ति निम्नलिखित वाक्य रचना के साथ मक्खी पर बनाई गई एक सरणी में निहित है:
squares = [x**2 for x in range(10)]
रूबी में एक एनालॉग होगा (इस धागे में एकमात्र पर्याप्त उत्तर, AFAIC):
a = Array.new(4).map{rand(2**49..2**50)}
उपरोक्त मामले में, मैं यादृच्छिक पूर्णांकों की एक सरणी बना रहा हूं, लेकिन ब्लॉक में कुछ भी हो सकता है। लेकिन यह एक रूबी सूची की समझ होगी।
मैंने इस विषय पर रेइन हेनरिक्स के साथ चर्चा की, जो मुझे बताते हैं कि सबसे अच्छा प्रदर्शन समाधान है
map { ... }.compact
यह अच्छा समझ में आता है क्योंकि यह मध्यवर्ती उपयोग के निर्माण से बचता है जैसा कि इसके अपरिवर्तनीय उपयोग के साथ होता है Enumerable#inject, और यह ऐरे को बढ़ने से रोकता है, जो आवंटन का कारण बनता है। जब तक आपके संग्रह में शून्य तत्व नहीं हो सकते, यह किसी भी अन्य के समान सामान्य है।
मैंने इसके साथ तुलना नहीं की है
select {...}.map{...}
यह संभव है कि रूबी का सी कार्यान्वयन Enumerable#selectबहुत अच्छा है।
एक वैकल्पिक समाधान जो हर कार्यान्वयन में काम करेगा और O (2n) समय के बजाय O (n) में चलेगा:
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
2चीजों nके बजाय बार 1बात nबार और फिर एक और 1बात यह है कि nकई बार :) में से एक महत्वपूर्ण लाभ inject/ reduceकि यह किसी भी बरकरार रखता है है nilइनपुट अनुक्रम जो और अधिक सूची-comprehensionly व्यवहार है में मान
मैंने अभी-अभी रूबी जीम्स को कॉम्प्रिहेंड रत्न प्रकाशित किया है , जो आपको ऐसा करने देता है:
require 'comprehend'
some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
यह C में लिखा है; सरणी केवल एक बार ट्रेस की गई है।
Enumerable की एक grepविधि है जिसका पहला तर्क एक विधेय खरीद हो सकता है, और जिसका वैकल्पिक दूसरा तर्क एक मैपिंग फ़ंक्शन है; तो निम्नलिखित काम करता है:
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
यह अन्य सुझावों के एक जोड़े के रूप में पठनीय नहीं है (मुझे एनोइक के सरल select.mapया हिस्टोक्रेट के समझदार मणि पसंद है), लेकिन इसकी ताकत यह है कि यह पहले से ही मानक पुस्तकालय का हिस्सा है, और एकल-पास है और इसमें अस्थायी मध्यवर्ती सरणियों को शामिल नहीं किया गया है , और इनयूज़िंग सुझावों nilमें इस्तेमाल किए गए आउट-ऑफ-बाउंड्स मूल्य की आवश्यकता नहीं है compact।
यह अधिक संक्षिप्त है:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
जैसा पेड्रो उल्लेख किया है, आप के लिए श्रृंखलित कॉल एक साथ फ्यूज कर सकते हैं Enumerable#selectऔर Enumerable#map, चयनित तत्वों पर एक ट्रेवर्सल से परहेज। यह सच है क्योंकि Enumerable#selectतह की विशेषज्ञता है या inject। मैंने जल्दबाजी में परिचय पोस्ट किया रूबी सब्रेडिट में विषय पर दिया।
मैन्युअल रूप से फ़्यूज़िंग एरे ट्रांसफ़ॉर्मेशन थकाऊ हो सकता है, इसलिए हो सकता है कि कोई व्यक्ति रॉबर्ट गैम्बल के comprehendकार्यान्वयन के साथ इसे select/ mapपैटर्न को पूर्ववर्ती बना सकता है।
कुछ इस तरह:
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?}
यह दृष्टिकोण करने का एक तरीका है:
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]
रूबी 2.7 पेश किया, filter_mapजो बहुत कुछ हासिल करता है जो आप चाहते हैं (मानचित्र + कॉम्पैक्ट):
some_array.filter_map { |x| x * 3 if x % 2 == 0 }
आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं ।
https://rubygems.org/gems/ruby_list_comprehension
मुहावरेदार रूबी सूची समझ की अनुमति देने के लिए मेरी रूबी सूची समझ रत्न के लिए बेशर्म प्लग
$l[for x in 1..10 do x + 2 end] #=> [3, 4, 5 ...]
मुझे लगता है कि सबसे अधिक सूची समझ-एस्क निम्नलिखित होगा:
some_array.select{ |x| x * 3 if x % 2 == 0 }
चूंकि रूबी हमें अभिव्यक्ति के बाद सशर्त जगह देने की अनुमति देती है, हमें सूची समझ के पायथन संस्करण के समान वाक्यविन्यास मिलता है। इसके अलावा, चूंकि selectविधि में ऐसा कुछ भी शामिल नहीं है जो इसके बराबर है false, सभी शून्य मान परिणामी सूची से हटा दिए जाते हैं और कॉम्पैक्ट पर कोई कॉल करना आवश्यक नहीं है, जैसा कि हमने इस्तेमाल किया था mapया collectइसके बजाय यह होगा।