2017-11-21 2 views
0

Ich fragte diese Art von Frage vor ein paar Tagen und irgendwie kam Kommunikation nicht zustande. Ich habe meine Frage so umstrukturiert, dass wir hoffentlich das Problem lösen können, das ich gelöst habe.So erstellen Sie eine aufzählbare Klasse ohne ein anderes Aufzählungszeichen?

Das Problem besteht im Wesentlichen darin: Wie erstellen Sie eine aufzählbare Klasse, die nicht nur ein anderes Aufzählungszeichen verwendet? Als vereinfachte, aber voll funktionsfähiges Beispiel dafür, was ich tun muss, diese Klasse betrachten:

class MyClass 
    include Enumerable 

    def get_next 
     return_val = rand.to_s 

     if return_val.match(/33/) 
      return nil 
     else 
      return return_val 
     end 
    end 

    def each 
     # ?????? 
    end 
end 

In dieser Klasse berechnet get_next einen Zufallswert. Wenn dieser Wert dann zwei 3 nebeneinander hat, gibt die Methode nil zurück. Andernfalls wird der Wert zurückgegeben. So können Sie get_next überfahren und über, in der Regel für weniger als zehn Mal: ​​

myob = MyClass.new 

while not myob.get_next.nil? 
    puts 'got one' 
end 

In Wirklichkeit get_next durch Tausende von Datenpunkten sucht. Wenn das nächste Objekt gefunden wird, wird ein ziemlich umfangreiches Objekt zurückgegeben. Es ist unmöglich, dass ein MyClass-Objekt in alle Elemente in einem Array schlüpft. Es muss nur einer nach dem anderen kommen.

In der Tat funktioniert meine Klasse gut, wie oben strukturiert. Ich möchte das jedoch auf eine rubinrische Art und Weise umsetzen.

Geben Sie also in der obigen Arbeitsklasse die Lücken ein. Was würde in jedem() dazu führen, dass dies eine aufzählbare Klasse wird?

+1

Werfen Sie einen Blick auf das Beispiel für ['Enumerator.new'] (http://ruby-doc.org/core-2.4.2/Enumerator.html # method-c-new) - Aufruf von 'take (10)' gibt nur die ersten 10 Elemente zurück, obwohl Zahlen in einer Endlosschleife zurückgegeben werden. Lesenswert: https://blog.arkency.com/2014/01/ruby-to-enum-for-enumerator/ – Stefan

Antwort

0
class MyClass 
    include Enumerable 

    def enum 
    @enum ||= Enumerator.new do |y| 
     loop do 
     value = get_next 
     raise StopIteration.new("nil") if value.nil? 
     y << value 
     end  
    end 
    end 

    def each 
    return enum_for(:each) unless block_given? 
    enum.each(&Proc.new) 
    end 

    def get_next 
    return_val = rand.to_s 
    return_val.match(/33/) ? nil : return_val 
    end 
end 

mc = MyClass.new 
mc.each { |e| puts e } 
#⇒ 0.9354594959902887 
# 0.6425492923024724 
# 0.24551401459676037 
# 0.17117672854168786 
# 0.15611540754420283 
# 0.3914491388594509 
# 0.40243662946930725 
# 0.3792875585148314 
# 0.11978219941961588 
# 0.49891129730441874 
+0

Fantastisch! Funktioniert genau wie ich brauche. Vielen Dank! – tscheingeld

2

füllen die Lücken. Was würde in jedem() dazu führen, dass dies eine aufzählbare Klasse wird?

Das Dokument sagt "The class must provide a method each, which yields successive members of the collection.". Sie können also einfach so lange Werte angeben, solange sie nicht nil sind.

def each 
    while val = get_next 
    yield val 
    end 
end 

Demo:

irb(main):002:0> MyClass.new.each { |e| puts e } 
0.9368650618038962 
0.9784286059513836 
0.9881624526762572 

Eine weitere Demo (eine Methode aus Enumerable zeigen):

irb(main):003:0> MyClass.new.map(&:to_f) 
=> [0.6368065208805999, 0.6926393662295052, 0.9911564544581742, 0.35788099831068787] 

Also das ist genug, um es "zählbare" zu machen, in dem Sinne, Mischen von Enumerable (und ich glaube nicht, dass es eine offizielle Ruby-Definition von "aufzählbar" gibt). Es ist nicht klar aus deiner Frage, ob du das wirklich brauchst, übrigens. Wenn Sie nur Werte mit each übergehen möchten, müssen Sie nicht einmal Enumerable einschließen. All das gibt Ihnen zusätzliche Methoden wie map und select. Selbst eine for Schleife funktioniert nur dank each, ohne einschließlich Enumerable:

irb(main):002:0> for e in MyClass.new 
irb(main):003:1> puts e 
irb(main):004:1> end 
0.7638166357043203 
0.18409214000273166 
0.39757329498164495 
0.14658393540113002 
0.948396753859117 
0.3157122887487579 
0.4804571082701993 

(aber beachten Sie, dass for Schleifen generell abgeraten werden. [1], [2] ich nur dies zeigt die Bedeutung der each und die weiter zu demonstrieren Unwichtigkeit von Enumerable für grundlegende Iteration.)

+0

... und wenn Sie möchten, dass 'each' ohne Argumente einen Enumerator zurückgibt (genau wie stdlib enumerables), fügen Sie' return to_enum es sei denn block_given? 'An den Anfang der Methode. – mwp

+0

Diese Implementierung ist nicht mit dem 'nächsten' Vertrag kompatibel, der erwartet, dass 'StopIteration' _thrown_ ist, wenn keine Elemente iteriert werden. Das heißt, diese Implementierung ist mit Ruby überhaupt nicht kompatibel und könnte viele versteckte Fehler enthalten, die sich später als Fehler bei der Produktionslaufzeit herausstellen werden. Der richtige Weg zu gehen ist ein "Enumerator", wie ich in meiner Antwort gezeigt habe, das ist schlicht falsch und noch schlimmer gefährlich. – mudasobwa

+0

@mudasobwa Huh? Ich mache genau das, was die Dokumentation sagt. Und * "überhaupt nicht mit Ruby kompatibel" *? Hast du den Teil übersehen, wo ich demonstriere, dass es funktioniert? * Mehr * als * du *, tu, da dein Demo nicht einmal etwas von'Enumerable' benutzt. Und "nächster" Vertrag? Es hat nicht einmal * eine 'nächste' Methode. Das ist ein 'Enumerator'-Ding, kein 'Enumerable'-Ding. Und wenn Sie ein Nicht-Block'Enumerator'-Ergebnis wünschen, fügen Sie einfach hinzu, was mwp gesagt hat. Du machst das sogar selbst (nur länger). Dann hätten Sie auch eine 'nächste', und es würde die' StopIteration' machen. Können Sie ein tatsächliches Problem zeigen? –

Verwandte Themen