为了进行等效于Python列表推导的操作,我正在执行以下操作:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
是否有更好的方法来执行此操作……也许只用一个方法调用?
为了进行等效于Python列表推导的操作,我正在执行以下操作:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
是否有更好的方法来执行此操作……也许只用一个方法调用?
Answers:
如果确实需要,可以创建一个像这样的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在ruby中是正确的。参见:gist.github.com/jfarmer/2647362
我做了一个快速基准测试,比较了这三种选择,而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(n)而不是O(2n)时间中运行的替代解决方案是:
some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}
2
的事情n
的时间,而不是1
事情n
次,然后另一1
件事n
倍:)的一个重要优点inject
/ reduce
是,它保留任何nil
到底哪个更列表comprehensionly行为的输入序列值
像这样:
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
在Array 上定义然后又有什么问题?(1..6).lazy{|x|x*3 if x.even?}
这是解决此问题的一种方法:
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
因此,基本上,我们将字符串转换为正确的ruby语法进行循环,然后可以在字符串中使用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]
或者,如果您不喜欢字符串的外观或必须使用lambda,我们可以放弃尝试镜像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
了几乎可以实现您想要的功能(map + compact):
some_array.filter_map { |x| x * 3 if x % 2 == 0 }
您可以在此处了解更多信息。
https://rubygems.org/gems/ruby_list_comprehension
我的Ruby List Comprehension gem的无耻插件,允许惯用的Ruby list comprehensions
$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值,并且不需要调用compact,就像我们曾经使用map
或collect
代替的情况一样。