[ruby] Ruby의 목록 이해

Python 목록 이해와 동등한 작업을 수행하기 위해 다음을 수행합니다.

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

이 작업을 수행하는 더 좋은 방법이 있습니까? 아마 하나의 메서드 호출로?



답변

정말로 원한다면 다음과 같이 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

나는 아마 당신이했던 방식대로 할 것입니다.


답변

어때?

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

적어도 내 취향에는 약간 깨끗하고 빠른 벤치 마크 테스트에 따르면 귀하의 버전보다 약 15 % 더 빠릅니다.


답변

세 가지 대안을 비교하는 빠른 벤치 마크를 만들었고 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


답변

목록 이해력이 무엇인지에 대해이 스레드에서 Ruby 프로그래머 사이에 약간의 혼란이있는 것 같습니다. 모든 단일 응답은 변환 할 기존 배열을 가정합니다. 그러나 목록 이해력의 힘은 다음 구문으로 즉석에서 생성 된 배열에 있습니다.

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

다음은 Ruby의 아날로그입니다 (이 스레드의 유일한 적절한 대답 인 AFAIC).

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

위의 경우 임의의 정수 배열을 생성하고 있지만 블록에는 모든 것이 포함될 수 있습니다. 그러나 이것은 Ruby 목록 이해입니다.


답변

Rein Henrichs와이 주제에 대해 논의했습니다. Rein Henrichs는 최고의 성능을내는 솔루션은

map { ... }.compact

이는의 변경 불가능한 사용과 같이 중간 배열을 빌드 Enumerable#inject하는 것을 피하고 할당을 유발하는 배열의 증가를 방지하기 때문에 합리적 입니다. 컬렉션에 nil 요소가 포함될 수없는 경우가 아니면 다른 것만 큼 일반적입니다.

나는 이것을 비교하지 않았다

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

Ruby의 C 구현 Enumerable#select도 매우 훌륭 할 수 있습니다.


답변

모든 구현에서 작동하고 O (2n) 시간 대신 O (n)에서 실행되는 대체 솔루션은 다음과 같습니다.

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


답변

방금 Comprehend gem 을 RubyGems에 게시 했습니다. 이렇게하면 다음과 같이 할 수 있습니다.

require 'comprehend'

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

C로 작성되었습니다. 어레이는 한 번만 순회됩니다.