当前位置:  开发笔记 > 编程语言 > 正文

在Ruby中列出理解

如何解决《在Ruby中列出理解》经验,为你挑选了8个好方法。

为了做相当于Python列表的理解,我正在做以下事情:

some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}

有没有更好的方法来做到这一点......也许有一个方法调用?



1> glenn mcdona..:

怎么样:

some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact

稍微清洁,至少根据我的口味,并根据快速基准测试比你的版本快约15%...


some_array.map {| x | x*3如果x%2 == 0} .compact也可以
以及`some_array.map {| x | x*3除非x%2} .compact`,这可以说是更具可读性/红宝石风格.
@nightpool`除非x%2`没有效果,因为0在红宝石中是真的.请参阅:https://gist.github.com/jfarmer/2647362

2> Robert Gambl..:

如果你真的想,你可以像这样创建一个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}```返回```[]```.
你可以使用紧凑!优化一下

3> knuton..:

我做了一个快速的基准比较三个替代品和map-compact似乎真的是最好的选择.

性能测试(Rails)

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


`inject` ==`reduce`

4> 小智..:

我与Rein Henrichs讨论了这个话题,他告诉我最好的解决方案是

map { ... }.compact`

这很有意义,因为它避免了构建中间数组和不可变的用法Enumerable#inject,并且它避免了增长数组,从而导致分配.除非您的集合可以包含零元素,否则它与其他任何一般一样.

我没有比较这个

select {...}.map{...}

Ruby的C实现Enumerable#select也可能非常好.



5> Mark..:

Ruby线程程序员在这个帖子中似乎有些混淆,关于列表理解是什么.每个响应都假定一些预先存在的数组进行转换.但是列表理解的强大之处在于使用以下语法动态创建的数组:

squares = [x**2 for x in range(10)]

以下是Ruby中的模拟(这个线程中唯一合适的答案,AFAIC):

a = Array.new(4).map{rand(2**49..2**50)} 

在上面的例子中,我正在创建一个随机整数数组,但该块可以包含任何内容.但这将是一个Ruby列表理解.


我不知道你的Ruby示例应该如何与你的Python示例类似.Ruby代码应为:squares =(0..9).map {| x | x**2}
虽然@michau是正确的,但列表理解的整个要点(Mark忽略了)是列表理解本身不使用不生成数组 - 它使用生成器和协同例程以流方式进行所有计算而根本不分配存储(除了临时变量)直到(iff)结果落在数组变量中 - 这是python示例中方括号的目的,将理解折叠为一组结果.Ruby没有类似于生成器的工具.
哦,是的,它(从Ruby 2.0开始):squares_of_all_natural_numbers =(0..Float :: INFINITY).lazy.map {| x | X**2}; p squares_of_all_natural_numbers.take(10).to_a
实际上我现在看到OP本身有一些作者想要转换的现有列表.但是列表理解的原型概念涉及通过引用一些迭代来创建一个之前不存在的数组/列表.但实际上,一些正式的定义表明列表理解根本不能使用map,所以即使我的版本也不是kosher,但我认为可以接近Ruby.

6> Pedro Morte ..:

一个替代解决方案将在每个实现中工作并在O(n)而不是O(2n)时间运行:

some_array.inject([]){|res,x| x % 2 == 0 ? res << 3*x : res}


你的意思是它只遍历列表一次.如果按照正式定义,O(n)等于O(2n).只是挑剔:)

7> histocrat..:

我刚刚向RubyGems 发布了理解gem,它允许你这样做:

require 'comprehend'

some_array.comprehend{ |x| x * 3 if x % 2 == 0 }

它是用C语言写的; 数组只遍历一次.



8> 小智..:

Enumerable有一个grep方法,其第一个参数可以是谓词proc,其可选的第二个参数是映射函数; 以下是有效的:

some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}

这不像其他几个建议那样可读(我喜欢anoiaque的简单select.map或histocrat的理解宝石),但它的优点是它已经是标准库的一部分,并且是单通道的,不涉及创建临时中间数组,并且不需要像-using建议nilcompact使用的越界值.

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有