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つのメソッド呼び出しで?
回答:
本当にやりたい場合は、次のように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!
変更されたアイテムがない場合、配列ではなくnilを返すため、機能しないと思います。
どのように「試合:
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が真であるため、@ nightpool は効果がありません。参照:gist.github.com/jfarmer/2647362
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
reduce
このベンチマークでも見ると面白いでしょう(stackoverflow.com/a/17703276を参照)。
inject
==reduce
このスレッドでは、リスト内包とは何かについて、Rubyプログラマの間で混乱が生じているようです。すべての単一の応答は、変換するいくつかの既存の配列を想定しています。しかし、リスト内包の力は、次の構文でその場で作成された配列にあります。
squares = [x**2 for x in range(10)]
以下はRubyの類似物です(このスレッドでの唯一の適切な答えはAFAICです):
a = Array.new(4).map{rand(2**49..2**50)}
上記の例では、ランダムな整数の配列を作成していますが、ブロックには何でも含めることができます。しかし、これはRubyリストの内包になります。
すべての実装で機能し、O(2n)時間ではなくO(n)で実行される代替ソリューションは次のとおりです。
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
2
事をn
代わりに倍1
もののn
倍とし、別1
のもののn
時間:) 1つの重要な利点のはinject
/ reduce
それがすべての保存していることであるnil
より多くのリスト、comprehensionly行動である入力シーケンス内の値
Enumerableには、grep
最初の引数が述語procであり、オプションの2番目の引数がマッピング関数であるメソッドがあります。したがって、次のように機能します。
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
これは他のいくつかの提案(私はanoiaqueの単純なselect.map
または歴史家の包括的なgemが好きです)ほど読みやすくはありませんが、その長所はすでに標準ライブラリの一部であり、一時的な中間配列の作成を含まないことです。 、および-using提案で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(:*))
このようなもの:
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つの方法です。
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
したがって、基本的には、文字列をループの適切なルビ構文に変換してから、文字列でpython構文を使用して実行できます。
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]
または、文字列の見え方が気に入らない場合や、ラムダを使用する必要がない場合は、Python構文をミラー化する試みを行わずに、次のようなことを行うことができます。
S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1]
# [0, 4, 8, 12, 16]
Ruby 2.7が導入されfilter_map
、あなたが望むものをほぼ実現しました(マップ+コンパクト):
some_array.filter_map { |x| x * 3 if x % 2 == 0 }
詳細については、こちらをご覧ください。
https://rubygems.org/gems/ruby_list_comprehension
慣用的なRubyリスト内包表記を可能にするRubyリスト内包gemの恥知らずなプラグ
$l[for x in 1..10 do x + 2 end] #=> [3, 4, 5 ...]
私が最も理解しているものは次のようになると思います:
some_array.select{ |x| x * 3 if x % 2 == 0 }
Rubyでは式の後に条件を置くことができるため、リスト内包表記のPythonバージョンと同様の構文が得られます。また、このselect
メソッドにはに相当するものが何も含まれていないためfalse
、結果のリストからすべてのnil値が削除され、map
またはを使用した場合のように、compactを呼び出す必要はありませんcollect
。