Python のリスト内包表記と同等の処理を行うために、次のようにしています。
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
これを行うためのより良い方法はありますか...おそらく1つのメソッド呼び出しで?
Python のリスト内包表記と同等の処理を行うために、次のようにしています。
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
これを行うためのより良い方法はありますか...おそらく1つのメソッド呼び出しで?
どうですか:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
少なくとも私の好みでは、わずかにきれいで、簡単なベンチマーク テストによると、あなたのバージョンよりも約 15% 高速です...
本当にしたい場合は、次のように 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
私はおそらくあなたがしたようにそれをするでしょう。
3 つの選択肢を比較する簡単なベンチマークを作成しましたが、map-compactが実際に最適なオプションのようです。
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
このスレッドでは、リスト内包表記とは何かについて、Ruby プログラマーの間で混乱が生じているようです。すべての応答は、変換する既存の配列を前提としています。しかし、リスト内包表記の力は、次の構文でオンザフライで作成される配列にあります。
squares = [x**2 for x in range(10)]
以下はRubyの類似物です(このスレッドでの唯一の適切な答え、AFAIC):
a = Array.new(4).map{rand(2**49..2**50)}
上記の場合、ランダムな整数の配列を作成していますが、ブロックには何でも含めることができます。しかし、これは Ruby のリスト内包表記になります。
私はこのトピックについてReinHenrichsと話し合いました。彼は、最高のパフォーマンスを発揮するソリューションは
map { ... }.compact
Enumerable#inject
これは、の不変の使用法のように中間配列を構築することを回避し、割り当ての原因となる配列の拡張を回避するため、理にかなっています。コレクションにnil要素を含めることができない限り、他の要素と同じくらい一般的です。
私はこれを比較していません
select {...}.map{...}
RubyのC実装Enumerable#select
も非常に優れている可能性があります。
すべての実装で機能し、O(2n) 時間ではなく O(n) で実行される代替ソリューションは次のとおりです。
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
Comprehend gemを RubyGems に公開したところ、次のことが可能になります。
require 'comprehend'
some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
C で書かれています。配列は 1 回だけトラバースされます。
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact
=> [6, 12, 18]
それは私にとってはうまくいきます。それもきれいです。はい、と同じですが、コードがわかりやすくなるmap
と思います。collect
select(&:even?).map()
以下でそれを見た後、実際には良く見えます。
これはより簡潔です:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
Enumerable#select
Pedro が述べたように、チェーンされたandへの呼び出しを融合してEnumerable#map
、選択した要素のトラバーサルを回避できます。Enumerable#select
は fold または の特殊化であるため、これは真ですinject
。Ruby subreddit に、このトピックについての拙速な紹介を投稿しました。
配列変換を手動で融合するのは面倒な場合があるため、誰かが Robert Gamble の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]
最もリストの理解に近いものは次のようになると思います。
some_array.select{ |x| x * 3 if x % 2 == 0 }
Ruby では式の後に条件を配置できるため、リスト内包表記の Python バージョンと同様の構文が得られます。また、select
メソッドには と同等のものは含まれないため、すべての nil 値が結果のリストから削除され、代わりにorfalse
を使用した場合のように、compact を呼び出す必要はありません。map
collect