Frage

das Äquivalent von Python Listenkomprehensionen zu tun, ich tue das folgende:

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

Gibt es einen besseren Weg, dies zu tun ... vielleicht mit einem Methodenaufruf?

War es hilfreich?

Lösung

Wenn Sie wirklich wollen, können Sie ein Array # comprehend Methode wie folgt erstellen:

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

Prints:

6
12
18

Ich würde wahrscheinlich tun es einfach so, wie Sie allerdings tat.

Andere Tipps

Wie wäre:

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

Ein wenig saubere, zumindest nach meinem Geschmack, und nach einem kurzen Benchmark-Test etwa 15% schneller als die Version ...

Ich habe eine schnelle Benchmark Vergleich die drei Alternativen und Karte-compact scheint wirklich die beste Option zu sein.

Performance-Test (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

Ergebnisse

/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

Ich besprach dieses Thema mit Rein Henrichs, der mir sagt, dass die leistungsstärksten Lösung

map { ... }.compact`

Dies ist sinnvoll, weil es vermeidet Zwischen Arrays als mit der unveränderlichen Nutzung von Enumerable#inject Aufbau und den Array vermeidet wächst, die Zuordnung verursacht. Es ist so allgemein wie alle anderen, es sei denn Ihre Sammlung nil Elemente enthalten kann.

Ich habe nicht verglichen diese mit

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

Es ist möglich, dass Rubys C Implementierung von Enumerable#select ist auch sehr gut.

Es scheint über einige Verwirrung bei den Ruby-Programmierern in diesem Thread zu sein, welche Liste Verständnis ist. Jede einzelne Antwort setzt eine gewisse vorher vorhandenen Array zu transformieren. Aber Kraft der Liste Verständnis liegt in einem Array im laufenden Betrieb mit folgender Syntax erstellt:

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

In dem folgenden würde eine analoge in Ruby (die einzige angemessene Antwort in diesem Thread, Darauf,) sein:

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

Im obigen Fall, Ich erstelle eine Reihe von Zufallszahlen, aber der Block alles enthalten könnte. Aber das wäre ein Ruby-Liste Verständnis sein.

Eine alternative Lösung, die in jeder Implementierung arbeiten und in O (n) anstelle von O (2n) Zeit laufen ist:

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

Ich habe gerade die gem zu begreifen RubyGems, was können Sie tun:

require 'comprehend'

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

Es ist in C geschrieben; das Array wird nur einmal durchlaufen.

Enumerable hat eine grep Methode, deren erstes Argument kann ein Prädikat proc sein und dessen optionale zweite Argument ist eine Abbildungsfunktion; so dass die folgenden Werke:

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

Das ist nicht so lesbar wie ein paar andere Vorschläge (Ich mag anoiaque ist ganz einfach select.map oder comprehend Juwel des histocrat), aber seine Stärken sind, dass sie bereits einen Teil der Standard-Bibliothek ist, und ist Single-Pass und ist nicht mit temporäre Zwischenarrays und einen out-of-Grenzen Wert wie nil nicht in den compact betriebenen Anregungen verwendet wurde, erfordern.

Dies ist prägnanter:

[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]

Das ist für mich funktioniert. Es ist auch sauber. Ja, es ist das gleiche wie map, aber ich denke, collect der Code verständlicher macht.


select(&:even?).map()

sieht eigentlich besser, nachdem sie unten zu sehen.

Wie Pedro erwähnt, können Sie die verkettete Anrufe verschmelzen Enumerable#select und Enumerable#map, eine Traversal über die ausgewählten Elemente zu vermeiden. Dies gilt, da Enumerable#select eine Spezialisierung von Falt- oder inject ist. Ich schrieb eine zum Thema bei der Ruby subreddit noreferrer"> eilten Einführung rel="nofollow.

Manueller Array-Transformationen Verschmelzen kann langwierig sein, also vielleicht jemand mit Robert Gamble comprehend Implementierung spielen könnte dies select / map Muster bisschen besser zu machen.

So etwas wie folgt aus:

def lazy(collection, &blk)
   collection.map{|x| blk.call(x)}.compact
end

Nennen Sie es:

lazy (1..6){|x| x * 3 if x.even?}

Welche zurück:

=> [6, 12, 18]

Eine andere Lösung, aber vielleicht nicht die beste

some_array.flat_map {|x| x % 2 == 0 ? [x * 3] : [] }

oder

some_array.each_with_object([]) {|x, list| x % 2 == 0 ? list.push(x * 3) : nil }

Ich denke, die meisten Liste Verständnis-esque folgend wäre:

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

Da Rubin uns erlaubt die bedingte nach dem Ausdruck zu bringen, erhalten wir eine Syntax wie die Python-Version der Liste Verständnis. Da auch die select Methode nichts enthält, die alle Null-Werte false entsprechen aus der resultierenden Liste entfernt und kein Aufruf zu kompakt ist notwendig, da der Fall wäre, wenn wir map oder collect stattdessen verwendet hatten.

scroll top