Rea*_*nly 86 ruby list-comprehension
为了做相当于Python列表的理解,我正在做以下事情:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来做到这一点......也许有一个方法调用?
gle*_*ald 84
怎么样:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
Run Code Online (Sandbox Code Playgroud)
稍微清洁,至少根据我的口味,并根据快速基准测试比你的版本快约15%...
Rob*_*ble 53
如果你真的想,你可以像这样创建一个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
Run Code Online (Sandbox Code Playgroud)
打印:
6
12
18
Run Code Online (Sandbox Code Playgroud)
我可能会按照你的方式做到这一点.
knu*_*ton 28
我做了一个快速的基准比较三个替代品和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
Run Code Online (Sandbox Code Playgroud)
/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
Run Code Online (Sandbox Code Playgroud)
小智 11
我与Rein Henrichs讨论了这个话题,他告诉我最好的解决方案是
map { ... }.compact`
Run Code Online (Sandbox Code Playgroud)
这很有意义,因为它避免了构建中间数组和不可变的用法Enumerable#inject,并且它避免了增长数组,从而导致分配.除非您的集合可以包含零元素,否则它与其他任何一般一样.
我没有比较这个
select {...}.map{...}
Run Code Online (Sandbox Code Playgroud)
Ruby的C实现Enumerable#select也可能非常好.
Mar*_*ark 10
Ruby线程程序员在这个帖子中似乎有些混淆,关于列表理解是什么.每个响应都假定一些预先存在的数组进行转换.但是列表理解的强大之处在于使用以下语法动态创建的数组:
squares = [x**2 for x in range(10)]
Run Code Online (Sandbox Code Playgroud)
以下是Ruby中的模拟(这个线程中唯一合适的答案,AFAIC):
a = Array.new(4).map{rand(2**49..2**50)}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我正在创建一个随机整数数组,但该块可以包含任何内容.但这将是一个Ruby列表理解.
一个替代解决方案将在每个实现中工作并在O(n)而不是O(2n)时间运行:
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
Run Code Online (Sandbox Code Playgroud)
我刚刚向RubyGems 发布了理解gem,它允许你这样做:
require 'comprehend'
some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
Run Code Online (Sandbox Code Playgroud)
它是用C语言写的; 数组只遍历一次.
小智 7
Enumerable有一个grep方法,其第一个参数可以是谓词proc,其可选的第二个参数是映射函数; 以下是有效的:
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
Run Code Online (Sandbox Code Playgroud)
这不像其他几个建议那样可读(我喜欢anoiaque的简单select.map或histocrat的理解宝石),但它的优点是它已经是标准库的一部分,并且是单通道的,不涉及创建临时中间数组,并且不需要像-using建议nil中compact使用的越界值.
小智 5
这更简洁:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
Run Code Online (Sandbox Code Playgroud)
Ruby 2.7 引入了filter_map它几乎实现了你想要的(地图+紧凑):
some_array.filter_map { |x| x * 3 if x % 2 == 0 }
Run Code Online (Sandbox Code Playgroud)
你可以在这里读更多关于它的内容。