Real applications of flip-flop in ruby
ruby inherited the Perl philosophy of having more than one way to do the same thing. I inherited that philosophy from Larry Wall, who is my hero actually. I want to make ruby users free. I want to give them the freedom to choose. Yukihiro Matsumoto
Ruby surely inherited a flip-flop operator from Perl, amongst others. Why? Because programming is sexy, ruby is sexy and—you get it—flip-flop is very sexy.
Uh. I probably should start with a brief explanation of what flip-flop is in general. (There is another great writing on the subject by Nithin Bekal.)
The term came from the electronics
where it roughly means two-state machine with memory. Basically, it’s meaning
can be expressed as a boolean variable that somehow depends on two conditions
and the history. It’s initiated with false
.
Once set to true
because of the first condition, it remains
true
until the second condition is evaluated to true
. Now it turns back
to false
until the first condition… I bet you got the point. Since it holds
the state and depends on it, flip-flop makes sense only inside loops.
The simplest implementation of flip-flop in ruby (if it had no dedicated syntax,) would be:
def flip_flop(enum, cond1, cond2, fun)
enum.inject(false) do |acc, value|
acc ||= cond1.(value)
fun.(value) if acc
acc && !cond2.(value)
end
end
and it might be used as:
enum = [2,1,1,2,2]
cond1 = ->(i) { i.odd? }
cond2 = ->(i) { i.even? }
fun = ->(value) { print value }
flip_flop(enum, cond1, cond2, fun)
#⇒ 112
In the code above, it turned to have truthy
state on the first odd number
and turned back to falsey
on the first even number. That simple.
Flip-flip out of the box
The thing is ruby comes with a default syntax for flip-flop operator, that looks a bit discouraging at first glance. It’s range operator with left range boundary being a first condition, and the right one—being the second.
cond1..cond2
Also, it is treated as flip-flop only inside conditionals and ternary operator. The example above might be rewritten using standard ruby syntax as:
[2,1,1,2,2].each do |i|
print i if i.odd?..i.even?
end
#⇒ 112
Cute, isn’t it?
Real applications
Well, this is all sexy, but hey what’s the real purpose of this? This operator is definitely not as common as most of others, but still.
Yesterday I needed to process a huge list of strings, finding matching pairs where one string comes after another (not necessarily immediately) and counting them. For those curious, it’s a sanity check for the FSM; basically I stress-tested the FSM, printed out the states and after all I wanted to make sure there were no state order violations (in this case the count should be zero if everything is fine.)
For the sake of brevity I will use integers in the example below. So, let’s say
we have a huge list of integers and we want to count how many times 2
comes
after 1
.
input = [3,1,2,2,1,1,1,3,2]
input.count { |i| i == 2 if (i==1)..(i==2) }
That’s basically it. Imagine, how clunky would look any other implementation, when you have seen this one.
Real applications …more
- split ini-file into sections (yeah, I hear your screaming “regexp,” but still)
- split log file on request IP / time change
- nearly everything that could be expressed as blah-blah-blah-boom.
I wish I could invent more of nifty examples, but as I said, this operator is not of very common usage. Still, it’s a must-know of every Ruby developer who pretends to have the Junior position outgrown.