I think the best solution would be to create some sort of objects to
handle being checked for position. This way, you can change the
appropriate methods based on your enum (sure, a little more work, but
you can avoid O(N^2) work for last operations if you're smart...)
Here's what I envisioned:
---- Enumerable Hack ----
module Enumerable
class PositionVariable
attr_reader :index, :enum
def initialize(idx, enum)
@index, @enum = idx, enum
end
def === (p)
meth = "index_#{p}?".intern
@enum.send(meth, @index)
end
def inspect
"#{@index.inspect} : #{@enum.inspect}"
end
def is?(index_check)
index_check.is?(self)
end
end
def Enumerable.make_index_const(namestr)
o = Object.new
o.instance_eval {@name = namestr}
class << o
def ===(positionVar)
self.is?(positionVar)
end
def to_s
@name
end
def is?(positionVar)
positionVar === self
end
end
o
end
def each_with_pos
fail "A block is needed for Enumerable#each_with_pos." unless block_given?
self.each_with_index {|e, i|
yield e, PositionVariable.new(i, self)
}
end
end
---- end Enumerable Hack ----
then, in my program, I have somethnig like this:
module Enumerable
FIRST, LAST, PRIME, EVEN, ODD = *%w{first last prime even odd}.map {|str|
Enumerable.make_index_const(str)
}
def index_first?(idx)
idx == 0
end
def index_last?(idx)
self.size == idx + 1
end
def index_even?(idx)
idx % 2 == 0
end
def index_odd?(idx)
!index_even?(idx)
end
def index_prime?(idx)
# standard prime test algorithm of your choice.
end
end
and then you can have something like this:
myenum.each_with_pos {|e, pos|
case pos
when Enumerable::FIRST then ...
when Enumerable::LAST then ....
when ...
end
if pos.is?(Enumerable::FIRST) then
...
else
...
end
}
it might be to `cluttery' for the enum namespace, though - but it's
also just dependent on you filling stuff out for the enums - or just
deciding to accept some defaults (like index_first? ...) (and
overriding the bad defaults when it comes time to...)
--
There's no word in the English language for what you do to a dead
thing to make it stop chasing you.