How to wrap either forkoff or map depending on a config variable?

Hello, good people of ruby-talk.

I’m trying to write an Enumerable#parallel method that, depending on
a config option (retrieved inside it) would end up being an alias for
either Enumerable#map or Enumerable#forkoff.

(I know calling enum.forkoff(1) should equal enum.map, but I need
the wrapper to call #map, as forkoff doesn’t work under JRuby.)

I’m quite new to the idea of blocks/closures, and while I love using
them, I’m still not sure how to write such a method. What I’m looking
for is something like this:

module Enumerable
def parallel &block
if Config.processes == 1
# call map with the passed block
else
# call forkoff with the passed block
end
end
end

Ideally, the call to forkoff would pass Config.processes as its
first param – i.e., calling enum.parallel {…} would mean calling
enum.forkoff(Config.processes) {…}

– Shot

Shot (Piotr S.):

I’m trying to write an Enumerable#parallel method that, depending on
a config option (retrieved inside it) would end up being an alias for
either Enumerable#map or Enumerable#forkoff.

FWIW, the spec to pass is

describe Enumerable do
it ‘should be able to process in parallel, depending on config option’
do
ArtDecomp::Config.processes = 1
pid1, pid2 = [1,2].parallel { Process.pid }
pid1.should == pid2
ArtDecomp::Config.processes = 2
pid1, pid2 = [1,2].parallel { Process.pid }
pid1.should_not == pid2
end
end

– Shot

Shot (Piotr S.):

Shot (Piotr S.):

I’m trying to write an Enumerable#parallel method that, depending on
a config option (retrieved inside it) would end up being an alias for
either Enumerable#map or Enumerable#forkoff.

FWIW, the spec to pass is

describe Enumerable do
it ‘should be able to process in parallel, depending on config option’ do
ArtDecomp::Config.processes = 1
pid1, pid2 = [1,2].parallel { Process.pid }
pid1.should == pid2
ArtDecomp::Config.processes = 2
pid1, pid2 = [1,2].parallel { Process.pid }
pid1.should_not == pid2
end
end

Ok, a patch to make it actually test the implementation:

describe Enumerable do
it ‘should be able to process in parallel, depending on config
option’ do
ArtDecomp::Config.processes = 1
pid1, pid2 = [1,2].parallel { Process.pid }

  • pid1.should == pid2
  • pid1.should == Process.pid
    ArtDecomp::Config.processes = 2
    pid1, pid2 = [1,2].parallel { Process.pid }
    pid1.should_not == pid2
    end
    end

– Shot (aware that the above spec in not really in true BDD spirit)

Hi,

To pass a block as a block to another function, put an ampersand before
the
variable name:

map &block
forkoff &block

If you are interested in performance, know that using the &block
notation
for blocks is slower than using the block_given? and yield semantics.

Dan

On Sun, May 18, 2008 at 4:03 PM, Shot (Piotr S.) [email protected]

Daniel F.:

To pass a block as a block to another function,
put an ampersand before the variable name

Thanks, this works perfectly now:

module Enumerable
def parallel &block
case ArtDecomp::Config.processes
when 1 then map &block
else forkoff ArtDecomp::Config.processes, &block
end
end
end

(I should probably put it in a module and extend the
relevant enums rather than monkeypatching Enumerable…)

If you are interested in performance, know that using the &block
notation for blocks is slower than using the block_given? and yield
semantics.

Thanks for the tip. In which cases does it matter? (In my very rough
benchmarks it doesn’t, but then the enum in question is rather small,
while the block’s contents take relatively long time to compute.)

How would I implement the above using block_given? + yield?

Sorry for being braindead here – I implemented #every_pair by wrapping
#each twice, but I’m a bit stumbled on how to handle #map’s/#forkoff’s
returns; cache them in an array? I tried wrapping my mind around this
but I still don’t feel familiar enough in the block/yield territory…
How would I pass the (variable number of) block’s arguments through
#parallel to the called methods? (I tried to come up with something
using *args but failed.)

– Shot

Shot (Piotr S.):

module Enumerable
def parallel &block
case ArtDecomp::Config.processes
when 1 then map &block
else forkoff ArtDecomp::Config.processes, &block
end
end
end

How would I implement the above using block_given? + yield?

It seems the below works:

module Enumerable
def parallel
case ArtDecomp::Config.processes
when 1 then map { yield }
else forkoff ArtDecomp::Config.processes { yield }
end
end
end

– Shot

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs