Forum: Ruby filtering blocks

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
6a5b5ed39eb9b91797e9e7b6f6362519?d=identicon&s=25 Maarten Mortier (maartenm)
on 2008-12-08 16:14
Is it possible to filter blocks, online, in an elegant way?

for instance if I have the block

class Integer
  def divisors
    (1..self).each{ |i| yield i if self % i == 0 }
  end
end

and I want to pass this block through an 'online' filter.

I want to do something elegant like
10.divisors.odd{ |n| puts n }
# instead of
10.divisors{ |n| puts n if n.odd? } #, which is pretty much the same as
10.divisors{ |n| yield n if n.odd? }{|n| puts n}

I suppose it's not possible at all to treat a block as an object in this
manner?

If so, suppose I want to use a generic filter that takes a block and a
condition as its arguments, and returns the block of each elements for
which a condition is fulfilled?

# pseudo
def filter (block, condition)
  block do |s|
    yield s if s.condition
  end
end

How would I do anything like this, without using arrays as inbetweens?
Am I again trying to treat blocks wrongly as objects, is it at all
possible to 'operate' through them in this way?
I'm new to ruby so this might be a bit stupid. I was just experimenting
and couldn't find an elegent, modular manner of filtering blocks
run-time in a 'chain-like' manner, without needlessly compressing them
to arrays in between.

I got this when googling, btw: www.rubyfilter.com :)
C4316b9567c49cb866b08003ed2687d0?d=identicon&s=25 Kai D. (kaid)
on 2008-12-08 17:35
Hm, I think the problem is not to filter a block (that's also possible,
because in ruby blocks are also objects of the class Proc). You want to
filter an Enumeration. Therefore you could use the class Enumerator.

require "enumerator"
class Integer
  def odds()
    if block_given? then
      1.upto(self) do |number| yield number if number % 2 == 1 end
    else
      Enumerable::Enumerator.new(self, :odds)
    end
  end
  def evens()
    if block_given? then
      1.upto(self) do |number| yield number if number % 2 == 0 end
    else
      Enumerable::Enumerator.new(self, :even)
    end
  end
end

5.odds.each do |number| puts number end
# => 1, 3, 5
5.odds.any? do |number| number % 3 == 0 end
# => true


And of course you can add more "filters" by extending the Enumerator
class, or better, a subclass of Enumerator which handles only with
Integers.

require "enumerator"
class Integer
  def odds()
    if block_given? then
      1.upto(self) do |number| yield number if number % 2 == 1 end
    else
      Enumerable::Enumerator::Integer.new(self, :odds)
    end
  end
  def evens()
    if block_given? then
      1.upto(self) do |number| yield number if number % 2 == 0 end
    else
      Enumerable::Enumerator::Integer.new(self, :even)
    end
  end
end
module Enumerable
  class Enumerator
    class Integer < Enumerator

      def prime
        if block_given? then
          each do |number|
            # primitive prime algorithm
            yield(number) unless (2..Math.sqrt(number)).any? do |i|
              number % i == 0
            end
          end
        else
          Enumerable::Enumerator.new(self, :prime)
        end
      end

      def odds
        if block_given? then
          each do |number| yield number if number % 2 == 1 end
        else
          Enumerable::Enumerator.new(self, :odds)
        end
      end

      def evens
        if block_given? then
          each do |number| yield number if number % 2 == 0 end
        else
          Enumerable::Enumerator.new(self, :evens)
        end
      end
    end
  end
end

12.odds.prime.each {|x| p x}
This topic is locked and can not be replied to.