Mirror318
Mirror318

Reputation: 12693

Ruby—integer substring (subbit?)

Substrings work where "hello"[0..2] returns "hel"

Is there an integer equivalent, that returns the sub-bits, as in 9[1..3] returns 4 or "100"?

Context:

I'm trying to do a genetic algorithm, and there is a cross over stage where you split two chromosomes in two, then swap halves, e.g. "10101010" crossed with "00000000" at i = 4 would be "00001010", "10100000"

However, I'm doing this a few million times, so using strings is inefficient, so is there a bitwise way to do this?

Upvotes: 0

Views: 149

Answers (1)

Cary Swoveland
Cary Swoveland

Reputation: 110685

You might consider refining the class Fixnum to permit Fixnum#[]'s argument to be either a bit index or a range of bit indices.

module StabBitRange
  def [](arg)
    case arg
    when Range
      mask = arg.reduce(0) { |n, idx| n |= (1 << idx) }
      (self & mask) >> arg.first
    else
      super
    end
  end
end

module StabBits
  refine Fixnum do
    prepend StabBitRange
  end
end

See Refinements and Module#prepend. Prepend StabBitRange moves the methods contained therein (here just one) before the methods with the same names in Fixnum in the ancestor chain. The alternative (commonly used before Prepend was introduced in Ruby v2.0) is to alias Fixnum#[] and then redefine Fixnum[] to be the new method that takes either a bit index or a range of bit indices. (See the edit history of this answer to see how that is done.) I expect readers will agree that Prepend is a more sensible way of doing that.

To make this code available for use we need only invoke the keyword using.

using StabBits

n = 682
n.to_s(2) #=> "1010101010" 

n[0]      #=> 0 
n[1]      #=> 1 
n[2]      #=> 0

n[0..7]   #=> 170 170.to_s(2) => "10101010" 
n[1..7]   #=> 85   85.to_s(2) => "1010101"
n[2..6]   #=> 10   10.to_s(2) => "1010"

When StabBitRange#\[\] is called and the argument is not a range, super invokes Fixnum#[] and passes the argument. That method handles argument errors as well as returning the desired bit when there are no errors.

When this code is run in IRB, the following exception is raised: RuntimeError: main.using is permitted only at top level. That's because IRB is running at the top level, but code run within IRB is running at a higher level from the perspective of the Ruby parser. To run this in IRB it is necessary to enclose using StabBits and the code following in a module.

Upvotes: 1

Related Questions