This one short-circuits, evaluating the left operand when the
flip-flop is false and the right operand when true:
class FlipFlop
attr_accessor :flip
@@FF = Hash.new {|h,k| h[k] = FlipFlop.new}
def FlipFlop.[](id)
@@FF[id]
end
def initialize
@flip = Proc.new if block_given?
@in_range = false
@num = 0
end
def flop(like=:awk)
if @in_range
@in_range = ! yield
else
@in_range = @flip[] or return
@in_range = ! yield if like == :awk
end
@num = String === @num ? 1 : @in_range ? @num + 1 : "#{@num + 1}E0"
end
def flop!(&b)
flop(:sed, &b)
end
end
module Kernel
def flip(id=caller[0])
FlipFlop[id].flip = Proc.new
FlipFlop[id]
end
end
puts ".. and ..."
a = (1..10).map {|i| i==3 .. (i+=0.5 and i==6.5) ? i : -i}
b = (1..10).map {|i| i==3 ... (i+=0.5 and i==6.5) ? i : -i}
p a, b
puts "#flip#flop and #flip#flop!"
a = (1..10).map {|i| flip {i==3}.flop {i+=0.5 and i==6.5} ? i : -i}
b = (1..10).map {|i| flip {i==3}.flop! {i+=0.5 and i==6.5} ? i : -i}
p a, b
Internal state is maintained using #caller info, so an explicit :label
is required if there is more one #flip#flop per line:
a = (1..10).select do |i|
flip(:x) {i==3}.flop {i==7} and flip(:y) {i==5}.flop {i==9}
end
Maybe useful, maybe not, but returns a sequence number, like Perl:
('a'..'z').each {|x|
i = flip {x=~/[ep]/}.flop {x=~/[iw]/}
puts "#{x}: #{i.inspect}"
}
Outputs:
a: nil
b: nil
c: nil
d: nil
e: 1
f: 2
g: 3
h: 4
i: "5E0"
j: nil
k: nil
l: nil
m: nil
n: nil
o: nil
p: 1
q: 2
r: 3
s: 4
t: 5
u: 6
v: 7
w: "8E0"
x: nil
y: nil
z: nil