Untuk melakukan yang setara dengan pemahaman daftar Python, saya melakukan yang berikut:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
Apakah ada cara yang lebih baik untuk melakukan ini ... mungkin dengan satu pemanggilan metode?
ruby
list-comprehension
Hanya baca
sumber
sumber
Jawaban:
Jika Anda benar-benar ingin, Anda dapat membuat metode Array #rehend seperti ini:
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
Cetakan:
6 12 18
Saya mungkin hanya akan melakukannya seperti yang Anda lakukan.
sumber
[nil, nil, nil].comprehend {|x| x }
yang kembali[]
.compact!
mengembalikan nol daripada array ketika tidak ada item yang diubah, jadi saya rasa itu tidak berhasil.Bagaimana jika:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
Sedikit lebih bersih, setidaknya menurut selera saya, dan menurut uji benchmark cepat sekitar 15% lebih cepat dari versi Anda ...
sumber
some_array.map{|x| x * 3 unless x % 2}.compact
, yang bisa dibilang lebih mudah dibaca / ruby-esque.unless x%2
tidak berpengaruh karena 0 benar dalam ruby. Lihat: gist.github.com/jfarmer/2647362Saya membuat patokan cepat yang membandingkan ketiga alternatif dan map-compact tampaknya menjadi pilihan terbaik.
Uji kinerja (Rel)
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
Hasil
/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
sumber
reduce
di benchmark ini juga (lihat stackoverflow.com/a/17703276 ).inject
==reduce
Sepertinya ada beberapa kebingungan di antara programmer Ruby di thread ini tentang pengertian list. Setiap respons tunggal mengasumsikan beberapa larik yang sudah ada sebelumnya untuk diubah. Tetapi kekuatan pemahaman daftar terletak pada larik yang dibuat dengan cepat dengan sintaks berikut:
squares = [x**2 for x in range(10)]
Berikut ini adalah analog di Ruby (satu-satunya jawaban yang memadai di utas ini, AFAIC):
a = Array.new(4).map{rand(2**49..2**50)}
Dalam kasus di atas, saya membuat array bilangan bulat acak, tetapi blok itu bisa berisi apa saja. Tapi ini akan menjadi pemahaman daftar Ruby.
sumber
Saya membahas topik ini dengan Rein Henrichs, yang memberi tahu saya bahwa solusi dengan kinerja terbaik adalah
Ini masuk akal karena menghindari pembuatan Array perantara seperti penggunaan yang tidak dapat diubah dari
Enumerable#inject
, dan menghindari menumbuhkan Array, yang menyebabkan alokasi. Ini umum seperti yang lainnya kecuali koleksi Anda dapat berisi elemen nihil.Saya belum membandingkan ini dengan
Ada kemungkinan implementasi Ruby C
Enumerable#select
juga sangat bagus.sumber
Solusi alternatif yang akan bekerja di setiap implementasi dan berjalan dalam waktu O (n) bukan O (2n) adalah:
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
sumber
2
sesuatun
kali daripada waktu1
bendan
dan kemudian1
hal lainn
kali :) Satu keuntungan penting dariinject
/reduce
adalah mempertahankannil
nilai apa pun dalam urutan masukan yang merupakan perilaku yang lebih memahami daftarSaya baru saja menerbitkan permata pemahaman ke RubyGems, yang memungkinkan Anda melakukan ini:
require 'comprehend' some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
Itu ditulis dalam C; larik hanya dilintasi sekali.
sumber
Enumerable memiliki
grep
metode yang argumen pertamanya bisa menjadi proc predikat, dan argumen kedua opsionalnya adalah fungsi pemetaan; jadi berikut ini bekerja:some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
Ini tidak terbaca seperti beberapa saran lain (saya suka permata sederhana
select.map
atau histokrat anoiaque), tetapi kekuatannya adalah bahwa itu sudah menjadi bagian dari pustaka standar, dan single-pass dan tidak melibatkan pembuatan array perantara sementara , dan tidak memerlukan nilai di luar batas seperti yangnil
digunakan dalamcompact
saran -menggunakan.sumber
Ini lebih singkat:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
sumber
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact => [6, 12, 18]
Itu berhasil untuk saya. Itu juga bersih. Ya, itu sama dengan
map
, tapi menurut sayacollect
membuat kodenya lebih mudah dimengerti.select(&:even?).map()
sebenarnya terlihat lebih baik, setelah melihatnya di bawah.
sumber
Seperti yang disebutkan Pedro, Anda dapat menggabungkan panggilan berantai ke
Enumerable#select
danEnumerable#map
, menghindari traversal pada elemen yang dipilih. Ini benar karenaEnumerable#select
merupakan spesialisasi dari lipatan atauinject
. Saya memposting perkenalan yang tergesa - gesa ke topik di subreddit Ruby.Penggabungan transformasi Array secara manual bisa membosankan, jadi mungkin seseorang bisa bermain-main dengan
comprehend
implementasi Robert Gamble untuk membuat polaselect
/ inimap
lebih cantik.sumber
Sesuatu seperti ini:
def lazy(collection, &blk) collection.map{|x| blk.call(x)}.compact end
Sebut saja:
lazy (1..6){|x| x * 3 if x.even?}
Yang mengembalikan:
=> [6, 12, 18]
sumber
lazy
di Array dan kemudian:(1..6).lazy{|x|x*3 if x.even?}
Solusi lain tetapi mungkin bukan yang terbaik
some_array.flat_map {|x| x % 2 == 0 ? [x * 3] : [] }
atau
some_array.each_with_object([]) {|x, list| x % 2 == 0 ? list.push(x * 3) : nil }
sumber
Ini adalah salah satu cara untuk mendekati ini:
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
jadi pada dasarnya kita mengubah string menjadi sintaks ruby yang tepat untuk loop maka kita dapat menggunakan sintaks python dalam string untuk melakukan:
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]
atau jika Anda tidak menyukai tampilan string atau harus menggunakan lambda, kami dapat mengabaikan upaya untuk mencerminkan sintaks python dan melakukan sesuatu seperti ini:
S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1] # [0, 4, 8, 12, 16]
sumber
Ruby 2.7 diperkenalkan
filter_map
yang cukup banyak mencapai apa yang Anda inginkan (peta + ringkas):some_array.filter_map { |x| x * 3 if x % 2 == 0 }
Anda dapat membaca lebih lanjut di sini .
sumber
https://rubygems.org/gems/ruby_list_comprehension
steker tak tahu malu untuk permata Pemahaman Daftar Ruby saya untuk memungkinkan pemahaman daftar Ruby idiomatik
$l[for x in 1..10 do x + 2 end] #=> [3, 4, 5 ...]
sumber
Saya pikir pemahaman daftar paling-esque adalah sebagai berikut:
some_array.select{ |x| x * 3 if x % 2 == 0 }
Karena Ruby memungkinkan kita untuk menempatkan kondisional setelah ekspresi, kita mendapatkan sintaks yang mirip dengan versi Python dari daftar pemahaman. Juga, karena
select
metode tidak menyertakan apa pun yang sama denganfalse
, semua nilai nihil dihapus dari daftar yang dihasilkan dan tidak diperlukan panggilan untuk memadatkan seperti yang akan terjadi jika kita telah menggunakanmap
ataucollect
sebagai gantinya.sumber