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