要做到Python的列表内涵相当于,我做了以下内容:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
有没有更好的办法用一个方法调用做到这一点...也许?
要做到Python的列表内涵相当于,我做了以下内容:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
有没有更好的办法用一个方法调用做到这一点...也许?
如果你真的想,你可以创建一个这样的数组#领悟方法:
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
我可能只是做它,你虽然做的方式。
怎么样:
some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact
稍微干净,至少我的口味,并根据快速基准测试比你的版本快15%左右...
我做了一个快速基准比较三种备选方案和地图小型似乎真的是最好的选择。
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
我讨论这个话题赖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的,它可以让你做到这一点:
require 'comprehend'
some_array.comprehend{ |x| x * 3 if x % 2 == 0 }
它是用C语言编写; 该阵列只穿过一次。
枚举具有grep
方法,其第一个参数可以是谓词PROC,并且其第二个可选参数是一个映射函数; 所以以下工作:
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
这不是一对夫妇的其他建议(我喜欢anoiaque很简单的可读性select.map
或histocrat的领悟宝石),但它的优势是它已经是标准库的一部分,并且是单通,不涉及创建临时的中间阵列,并且不需要外的边界的值等nil
在所使用的compact
-使用的建议。
这是更简洁:
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact
=> [6, 12, 18]
这对我行得通。 这也是干净的。 是的,这是一样的map
,但我觉得collect
使代码更容易理解。
select(&:even?).map()
实际上看起来更好,下面看到它之后。
像佩德罗提到的,可以融合在一起的链式调用Enumerable#select
和Enumerable#map
,避免了在选定的元素遍历。 因为这是真正的Enumerable#select
是倍或的专业化inject
。 我贴一个仓促推出的红宝石版(Subreddit)的话题。
手动融合阵列转换可能是乏味的,所以也许有人可以与罗伯特宝洁公司发挥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.flat_map {|x| x % 2 == 0 ? [x * 3] : [] }
要么
some_array.each_with_object([]) {|x, list| x % 2 == 0 ? list.push(x * 3) : nil }
我认为最列表解析式的将是如下:
some_array.select{ |x| x * 3 if x % 2 == 0 }
由于红宝石允许我们把条件表达式后,我们得到类似于列表理解的Python版本的语法。 此外,由于select
方法不包括任何等同于false
,所有的零值从结果列表中删除,并以紧凑没有呼叫是必要的,如果我们作过会是这样map
或collect
来代替。