2010-04-07 74 views
4

我有一類具有自定義各法:呼籲迭代器塊方法:each.magic.collect {...}

class CurseArray < Array 
    def each_safe 
     each do |element| 
      unless element =~ /bad/ 
       yield element 
      end 
     end 
    end 
end 

而且要撥打不同的塊的方法,如「收「或」注入「這些迭代元素。例如:

curse_array.each_safe.magic.collect {|element| "#{element} is a nice sentence."} 

我知道有一個特定的功能(我稱之爲「神奇」在這裏)要做到這一點,但我已經忘記了。請幫忙! :-)

回答

6

如果一個方法可以得到你需要它傳遞一個塊。沒有辦法定義自動傳遞的塊。

最近我能到你的規格是:

def magic(meth) 
    to_enum(meth) 
end 

def test 
    yield 1 
    yield 2 
end 

magic(:test).to_a 
# returns: [1,2] 

實現您的要求的清潔方法可能是:

class MyArray < Array 
    def each_safe 
    return to_enum :each_safe unless block_given? 
    each{|item| yield item unless item =~ /bad/} 
    end 
end 

a = MyArray.new 
a << "good"; a << "bad" 
a.each_safe.to_a 
# returns ["good"] 
+0

這就是我想要的,謝謝! – blinry 2010-04-08 10:35:55

2

你寫你的each_safe法的方式,最簡單的是

curse_array.each_safe { |element| do_something_with(element) } 

編輯:哦,你的each_safe方法是不正確的,無論是。它必須是「每一個做」,而不是「each.do」

編輯2:如果你真的希望能夠做的事情,如「each_safe.map」,而在同一時間也能夠做到「 each_safe { ... }「你可以寫你的方法是這樣的:

require 'enumerator' 

class CurseArray < Array 
    BLACKLIST = /bad/ 
    def each_safe 
    arr = [] 
    each do |element| 
     unless element =~ BLACKLIST 
     if block_given? 
      yield element 
     else 
      arr << element 
     end 
     end 
    end 

    unless block_given? 
     return Enumerator.new(arr) 
    end 
    end 
end 
+0

謝謝。我打算使用=〜來進行正則表達式匹配。 您描述的方式是我現在怎麼做,但它不是很優雅。 – blinry 2010-04-07 10:09:55

+0

@blinry使用'=〜'和'Regexp'是正確的,因爲您可能想要在黑名單中添加更多單詞。 @ dominikh讓'each_safe'在有或沒有塊的情況下工作是一個好方法。 – 2010-04-07 13:14:28

+1

構建答案然後返回一個枚舉器是個不錯的主意。統計員的整個想法是有懶惰的評價!如果在調用'each_safe'之後但在使用枚舉器之前修改了數組?如果這是一個長文本,我們只需要「第一(3)」呢?在下面檢查Sam的答案。 – 2010-04-07 16:22:18

0

選擇的解決方案採用了常見的成語to_enum :method_name unless block_given?它的確定,但有其他選擇:

  1. 給你的「不友好的」yie lder方法不變,調用時使用enum_for

  2. 使用懶惰Enumerator

  3. 使用惰性數組(需要Ruby 2.0或gem enumerable-lazy)。

這裏有一個演示代碼:

class CurseArray < Array 
    def each_safe 
    each do |element| 
     unless element =~ /bad/ 
     yield element 
     end 
    end 
    end 

    def each_safe2 
    Enumerator.new do |enum| 
     each do |element| 
     unless element =~ /bad/ 
      enum.yield element 
     end 
     end 
    end 
    end 

    def each_safe3 
    lazy.map do |element| 
     unless element =~ /bad/ 
     element 
     end 
    end.reject(&:nil?) 
    end 
end 

xs = CurseArray.new(["good1", "bad1", "good2"]) 
xs.enum_for(:each_safe).select { |x| x.length > 1 } 
xs.each_safe2.select { |x| x.length > 1 } 
xs.each_safe3.select { |x| x.length > 1 }.to_a