2016-04-05 7 views
2

Ich brauchte eine Funktion, die Array#select ähnlich ist, aber übergibt nicht nur das Datenelement, sondern auch den Index des Elements (ähnlich Enumerable#each_with_index). Ich versuchte dies:Ruby: Kombinieren von Enumeratoren, Erstellen eines neuen Enumerators

['a','b','a','a','c'].select_with_index {|letter,index| letter == 'a' && index > 1 } 

Ergebnisse in

["a", "a"] 

wie erwartet:

class Array 
    def select_with_index 
    self.each_with_index.select {|*args| yield(*args)}.map(&:first) 
    end 
end 

Diese in der Tat scheint zu funktionieren. Was mir aber mit meiner Lösung nicht gefällt ist, dass man einen Block liefern lässt. Ähnliche Methoden im Ruby-Core können ohne Block aufgerufen werden und ergeben eine Enumerator. Wie kann ich das machen? Ich weiß, dass ich block_given? verwenden kann, um auf das Vorhandensein des Blocks zu testen, aber wie mache ich dann weiter? Brauche ich eine Fiber?

BTW, sollte der Code mit beiden Ruby 1.9.3 arbeiten. und 2.x.

Antwort

2

Sie benötigen keine eigene Methode. Sie können die Tatsache nutzen, dass select einen Enumerator zurück, wenn ohne einen Block genannt wird, so kann man einfach ein with_index oben schlagen:

p ['a','b','a','a','c'].select.with_index {|letter,index| letter == 'a' && index > 1 } 
# >> ["a", "a"] 

Wenn Sie wirklich wollen, dass diese ein Verfahren auf Array sein (für dynamischen Aufruf und was nicht), das ist trivial:

class Array 
    def select_with_index(&block) 
    select.with_index(&block) 
    end 
end 

p ['a','b','a','a','c'].select_with_index {|letter,index| letter == 'a' && index > 1 } 
p ['a','b','a','a','c'].select_with_index 
# >> ["a", "a"] 
# >> #<Enumerator: #<Enumerator: ["a", "b", "a", "a", "c"]:select>:with_index> 
+0

Es ist schwer zu verwenden in Metaprogrammierung, e. G. in irgendwie 'public_send selector_name, & λ'. – mudasobwa

+0

Nun, gib diesem Namen dann einen Namen: http://pastie.org/10786273 –

3
class Array 
    def select_with_index 
    #  ⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓ this 
    return enum_for(:select_with_index) unless block_given? 
    each_with_index.select {|*args| yield(*args)}.map(&:first) 
    end 
end 
Verwandte Themen