hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Hopscotching Arrays with Flip-Flops #

by why in inspect

Hehe. You’re going to love this! (Warning: some of you are going to hate this.) Watch me skip every other element in an array with a flip-flop.

 >> s = true
 >> (1..10).reject { true if (s = !s) .. (s) }
 => [1, 3, 5, 7, 9]

Well, actually, that’s not so neat, since such a thing can simply be done with a single assignment in the block.

 >> s = true
 >> (1..10).reject { s = !s }
 => [1, 3, 5, 7, 9]

BUT! BUT! Tell me, can you hop to every third element in an array using only booleans and assignment? My flip-flops can jump that much higher.

 >> s = true
 >> a = (1..10).reject { true if (s = !s) .. (s = !s) }
 => [1, 4, 7, 10]

Now, let’s see you beat this reassembling of our array into threes. My flip-flops bunch triplets together!

 >> s = true
 >> a = (1..10).inject([]) do |ary, v| 
 >>       ary << [] unless (s = !s) .. (s = !s); ary.last << v; ary
 >> end
 => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Well, maybe this one you can beat with a modulo, but mine will be more detestable for sure.

said on 16 May 2005 at 16:50

the last one is hardcore: i hate this :)

said on 16 May 2005 at 16:51

I don’t understand how this works. If I try to construct a range as:


true..false

Ruby complains:


irb(main):061:0> true..false
ArgumentError: bad value for range
        from (irb):61
        from (null):0

said on 16 May 2005 at 18:19

if bool..bool has a special meaning, though I can’t remember what it is exactly. It’s so cryptic I decided not to use it a long time ago.

said on 16 May 2005 at 18:28

I love it! What a mess of obfuscated glory…a cute little game we have in these range conditions.

Now here’s the solution to split in bits of eight entries:

s = true
a = (1..64).inject([]) do |ary, v|
    unless (s ^= v[2].zero?)...(s ^= !v[1].zero?)
        ary << []
    end
    ary.last << v
    ary
end
p a

Please, don’t show this to Nubys. The may jump from a bridge…or even turn to Java…

said on 16 May 2005 at 19:21

John Wilder: the double-dot is a flip-flop operator when used in a conditional. The flip-flop is turned on by the first expression’s truth and turned off by the second expression’s truth. The tricky thing about the flip-flop is that it keeps the on/off state inside, so once it’s turned on by the opening expression, it says on until the closing expression is proven true.

‘Taint harmful. ‘Tis part of Ruby’s histree!!

said on 16 May 2005 at 20:35

You’ve (red) handed all those obfuscated Ruby programmers another intoxicated rabbit they can pull out of their fedoras.

I tips me hat to you!

said on 16 May 2005 at 20:38

You’ve (red) handed all those obfuscated Ruby programmers another intoxicated rabbit they can pull out of their fedoras.

I tips me hat to you!

(Sorry if this a dupe – I posted it before but it didn’t appear in the comments)

said on 16 May 2005 at 22:01

‘Taint harmful. ‘Tis part of Ruby’s histree!!

Correct, why, but there are elements within the Ruby community pushing for the elimination of the flip-flop operator in future versions of Ruby. They seek to eliminate it because to them it is some sort of strange Perlish thing. Yes, it is Perlish (Perl invented it from what I can tell), and it might seem a bit strange if you’re not used to it, but the flip-flop is mighty powerful!

Save the flip/flop op!

said on 16 May 2005 at 23:18
Modulo? Tssk, the simple way would be something like:
 
a = (1..10).to_a
a.each_index{|i| a[i,3] = [a[i,3]]}
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

said on 17 May 2005 at 10:49

Hmm, wow. So, how do flip-flops interact with e.g. threads and continuations?

To wit: in the case of threads, is the state thread-local or global? Can they be reified as objects?

I suspect the answers would be global, and no (I’ll have to play around a bit to verify later).

If true, the flip-flop is quite like the regexp in Perl before they were finally made first-class objects. Phantom figments of the parse tree, stateful, but unincarnate.

We did it for Range—let’s have a FlipFlop class!

said on 17 May 2005 at 11:31

Aha, Ruby does flip-flops correctly. Better than thread-local, even.

Flip-flop state for each method invocation is separate, but can be captured by blocks (as why’s examples demonstrate).

said on 17 May 2005 at 13:53

Incidentally, here’s another way to skip by threes:


>> s = true
>> (1..10).reject { true if (s = !s) ... (s) }
=> [1, 4, 7, 10]

(note extra dot)

said on 17 May 2005 at 14:26

A key to understanding Ruby flip-flops is that the “off” test never has an immediate effect on the result.

The difference between .. and … is that .. will try the “off” test within the same iteration if the “on” test succeeds, whereas … will wait for the next iteration to try.

And here’s the promised FlipFlop class:


class FlipFlop
  # corresponds to ..
  def test2(on, off)
    @state = on.call unless @state
    if @state
      @state = !off.call
      true
    else
      false
    end
  end
  # corresponds to ...
  def test3(on, off)
    if @state
      @state = !off.call
      true
    else
      @state = on.call
    end
  end
end

So, why’s last example could be rewritten:


>> s = true
>> f = FlipFlop::new
>> a = (1..10).inject([]) do |ary, v| 
>>      ary << [] unless f.test2(lambda {s = !s}, lambda {s = !s}); ary.last << v; ary
>> end
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Of course this is ugly. Still far too much sand between the toes.

Anyone have any ideas for making it nicer syntactically?

said on 18 May 2005 at 23:31

I love it. Gentle reminder: built-in to lump / slice n-tuples:

require ‘enumerator’ list.each_slice(n) ## gives you top-level list of lists

said on 21 May 2005 at 09:48

two, three, four…


>> s = true
>> (1..20).reject { true if (s = !s) .. (s = !s) and (s = !s) ... (s) }
=> [1, 5, 9, 13, 17]
said on 24 May 2005 at 00:18

There was a lot of discussion about the flip-flop-op on the list quite a while back… I seem to remember Matz saying that he didn’t like it and it would be removed. I wonder what ever became of that?

Comments are closed for this entry.