2010-08-03 11 views
26

Wie bekomme ich ein lazy-Array in Ruby?Wie bekomme ich ein lazy-Array in Ruby?

In Haskell kann ich über [1..] sprechen, die eine unendliche Liste ist, faul generiert wie benötigt. Ich kann auch Sachen wie iterate (+2) 0 machen, die jede Funktion anwenden, die ich ihm gebe, um eine faule Liste zu erzeugen. In diesem Fall würde es mir alle geraden Zahlen geben.

Ich bin mir sicher, ich kann solche Dinge in Ruby tun, aber kann nicht scheinen, wie es funktioniert.

+2

In Bezug auf Lazy Arrays: Arrays unterscheiden sich erheblich von Listen. Eine Implementierung von Lazy-Arrays, die unendliche Arrays zulassen würden, hätte schreckliche Laufzeiteigenschaften. – sepp2k

Antwort

39

Mit Ruby 1.9 können Sie die Enumerator-Klasse verwenden. Dies ist ein Beispiel aus der Dokumentation:

fib = Enumerator.new { |y| 
    a = b = 1 
    loop { 
     y << a 
     a, b = b, a + b 
    } 
    } 

    p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 

Auch dies ein schöner Trick ist:

Infinity = 1.0/0 

    range = 5..Infinity 
    p range.take(10) #=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 

Dieser funktioniert nur wenn aufeinanderfolgende Werte.

+4

sie müssen nicht konsekutiv sein: (10..100) .Schritt (20) .Take (5) # => [10, 20, 30, 40, 50 ] – horseyguy

+2

In Ruby 1.8 können Sie 'Backports' anfordern, um diese Funktionalität zu erhalten :-) –

+2

Beachten Sie, dass 'fib.map {| x | x + 1} .take (10) 'funktioniert nicht, weil map versucht, ein Array zu erstellen. Beachten Sie auch, dass die Elemente zweimal berechnet werden, wenn Sie 'fib.take (10)' zweimal ausführen (im Gegensatz zu faulen Listen, in denen Elemente nach der Berechnung im Speicher gehalten werden). Das ist also nicht gerade faulen Listen. – sepp2k

-3

Ruby Arrays werden bei Bedarf dynamisch erweitert. Sie können Blöcke auf sie anwenden, um Dinge wie gerade Zahlen zurückzugeben.

array = [] 
array.size # => 0 
array[0] # => nil 
array[9999] # => nil 
array << 1 
array.size # => 1 
array << 2 << 3 << 4 
array.size # => 4 

array = (0..9).to_a 
array.select do |e| 
    e % 2 == 0 
end 

# => [0,2,4,6,8] 

Does diese Hilfe?

+0

danke - glaube an deine Träume – carlfilips

+2

War nicht die Frage über * faul * Generation von Objekten? Die richtige Lösung besteht nicht aus Arrays (die in Ruby immer vollständig bestimmt sind), sondern aus anderen Typen von "Enumerable", wie in Sanjanas Antwort. –

+0

Ich begann gerade Ruby, ich dachte, das war richtig. Ich werde das beheben - können Ihre Wünsche in Erfüllung gehen – carlfilips

5

Faule Bereich (natürliche Zahlen):

Inf = 1.0/0.0 
(1..Inf).take(3) #=> [1, 2, 3] 

Faule Bereich (gerade Zahlen):

(0..Inf).step(2).take(5) #=> [0, 2, 4, 6, 8] 

Hinweis, können Sie auch Enumerable mit einigen Methoden erweitern mit faulen Bereichen, um die Arbeit (und usw.) bequemer:

module Enumerable 
    def lazy_select 
    Enumerator.new do |yielder| 
     each do |obj| 
     yielder.yield(obj) if yield(obj) 
     end 
    end 
    end 
end 

# first 4 even numbers 
(1..Inf).lazy_select { |v| v.even? }.take(4) 

output: 
[2, 4, 6, 8] 

mehr Infos hier: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/

Es gibt auch Implementierungen von lazy_map und lazy_select für die Enumerator Klasse, die hier gefunden werden kann: http://www.michaelharrison.ws/weblog/?p=163

+0

Beachten Sie, dass Sie, wie bei der Enumerator-Lösung, keine Dinge wie 'infinite_range.filter {| x | f (x)}. take (5) ', so verhält es sich nicht wie faule Listen. – sepp2k

+0

@ sepp2k, Link zur Site hinzugefügt, die Implementierungen von 'lazy_select' usw. für die 'Enumerator'-Klasse enthält – horseyguy

1

Wie ich bereits gesagt, in meinen Kommentaren, wäre so etwas wie faul Arrays Implementierung nicht sein sinnvoll. Die Verwendung von Enumerable kann in einigen Situationen gut funktionieren, unterscheidet sich aber in einigen Punkten von Lazy Lists: Methoden wie map und filter werden nicht träge ausgewertet (so dass sie nicht bei unendlichen Enumerables funktionieren) und Elemente, die es gewesen sind Einmal berechnet, werden nicht gespeichert. Wenn Sie also zweimal auf ein Element zugreifen, wird es zweimal berechnet.

Wenn Sie das genaue Verhalten von haskells faulen Listen in Ruby wollen, gibt es eine lazylist gem, die faule Listen implementiert.

19

Kürzlich Enumerable :: Lazy wurde zum Rubinstamm hinzugefügt. Wir werden es in Ruby 2.0 sehen. Insbesondere:

a = data.lazy.map(&:split).map(&:reverse) 

nicht sofort ausgewertet werden.
Das Ergebnis ist Instanz von Enumerable :: Lazy, das kann weiter faul angekettet werden.Wenn Sie ein aktuelles Ergebnis erhalten möchten - verwenden #to_a, #take(n) (#take auch jetzt faul ist, verwenden #to_a oder #force) usw.
Wenn Sie mehr zu diesem Thema und meine C-Patch wollen - siehe mein Blog-Post Ruby 2.0 Enumerable::Lazy

1

Diese Schleife wird bis ins Unendliche:

0.step{|i| puts i} 

Diese Schleife wird bis ins Unendliche zweimal so schnell:

0.step(nil, 2){|i| puts i} 

Dies wird ins Unendliche gehen, nur wenn Sie es wollen (ergibt einen Enumerator).

table_of_3 = 0.step(nil, 3) 
Verwandte Themen