Python 목록 이해와 동등한 작업을 수행하기 위해 다음을 수행합니다.
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
이 작업을 수행하는 더 좋은 방법이 있습니까? 아마 하나의 메서드 호출로?
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
나는 아마 당신이했던 방식대로 할 것입니다.
[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은 루비에서 진실이기 때문에 @nightpool 은 효과가 없습니다. 참조 : 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 목록 이해입니다.
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}
2
일 n
시간 대신 1
일을 n
하고 다른 1
일 n
시간을합니다. inject
/의 중요한 장점 중 하나 는 입력 시퀀스에서 더 많은 목록을 이해하는 동작 인 reduce
모든 nil
값을 보존한다는 것입니다.
Enumerable에는 grep
첫 번째 인수가 술어 proc이 될 수 있고 선택적 두 번째 인수가 매핑 함수 인 메서드가 있습니다. 따라서 다음이 작동합니다.
some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}
이것은 몇 가지 다른 제안 (나는 anoiaque의 simple select.map
또는 histocrat의 comprehend gem을 좋아함)만큼 읽기 쉽지는 않지만, 그것의 강점은 그것이 이미 표준 라이브러리의 일부이고 단일 패스이고 임시 중간 배열을 만드는 것을 포함하지 않는다는 것입니다. 이며 -using 제안에 nil
사용 된 것과 같은 범위를 벗어난 값이 필요하지 않습니다 compact
.
이것은 더 간결합니다.
[1,2,3,4,5,6].select(&:even?).map{|x| x*3}
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
이 같은:
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
그래서 기본적으로 우리는 문자열을 루프를위한 적절한 루비 구문으로 변환하고 있습니다. 그러면 우리는 문자열에서 파이썬 구문을 사용하여 할 수 있습니다 :
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]
또는 문자열의 모양이 마음에 들지 않거나 람다를 사용해야하는 경우 파이썬 구문을 미러링하려는 시도를 포기하고 다음과 같이 할 수 있습니다.
S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1]
# [0, 4, 8, 12, 16]
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 값이 제거되고 사용 map
했거나 collect
대신 사용한 경우처럼 compact에 대한 호출이 필요하지 않습니다 .