0

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?

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Good answer, three comments: #1 that's what prepend is for, #2 that's what Refinements are for, #3 Fixnum is gone as of Ruby 2.4, and never was part of the official spec (it was an "implementation-defined subclass"), use Integer instead.
Readers, I have incorporated @JörgWMittag's very useful suggestions. See the edit history if you wish to see what I had before. Jörg, any further suggestions? I understand that in v2.4+ Fixnum will be an alias of Integer. Even then, I would still need refine Fixnum do rather than refine Integer do to maintain compatibility with pre-v2.4 Ruby (even though Fixnum will be gone). And all the more reason to use kind_of?(Integer). Correct?
Yeah, I noticed that refining Integer doesn't quite work. The Language Spec says that there must be either an Integer class with no subclasses or Integer must be abstract with implementation-defined subclasses. In the former case, refining it does work, but in the latter case, the definition gets overridden by the ones in the implementation-defined subclasses. Meh.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.