-
-
Save divoxx/391450 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Between those alternatives I'd go with this one. | |
# | |
# Ruby has open classes, there is nothing wrong about embracing them. Even Rails that used to | |
# create modules and mix them into the classes has decided to change it on rails3 and simply | |
# open the classes and add the methods. | |
# | |
# It's simple, and it works. Of course you shouldn't be adding stuff that are specific to | |
# your application's business logic. | |
class Fixnum | |
def to_bit_array | |
to_s(2).reverse.split("").map(&:to_i) | |
end | |
end | |
# Which is why I actually would change this method. Since a Fixnum is a sequence of bytes | |
# already and you just need to check if some bits are on, then I would change the method to | |
# this: | |
class Fixnum | |
# Returns true if all the given bits positions are on (set to 1) | |
# Example: | |
# 0b001110.bits_on?(1, 2, 3) | |
def bits_on?(*bits) | |
bits.all? { |n| self[n] == 1 } | |
end | |
# Adds a singular alias | |
alias_method :bit_on?, :bits_on? | |
end | |
# If you actually need to perform more calculations over the sequence of bits, I'd create a | |
# separate class for it but using composition instead of inheritance. | |
# | |
# Example: | |
# m = BitMap.new(0b001110) | |
# m.bits_on # => [1, 2, 3] | |
# m.bits_on?(1,2,3) # => true | |
class BitMap | |
def initialize(integer) | |
@integer = integer | |
end | |
# Returns true if all the given bits positions are on (set to 1) | |
# Example: | |
# 0b001110.bits_on?(1, 2, 3) | |
def bits_on?(*bits) | |
bits.all? { |n| @integer[n] == 1 } | |
end | |
# Adds a singular alias | |
alias_method :bit_on?, :bits_on? | |
# Return all the bits that are on | |
# Example: | |
# 0b001110.bits_on # => [1,2,3] | |
def bits_on | |
indexes = [] | |
(0...@integer.size*8).each { |n| indexes << n if @integer[n] == 1 } | |
indexes | |
end | |
end |
Oh, and if the bit string was bigger then transforming the integer to a string, then to an array, then reverse, then map would definitely take longer.
Using the number 0xffffffffffffffffffffffff:
Rehearsal --------------------------------------------------- Module method : 0.000000 0.000000 0.000000 ( 0.000200) BitMap class : 0.000000 0.000000 0.000000 ( 0.000121) ------------------------------------------ total: 0.000000sec user system total real Module method : 0.000000 0.000000 0.000000 ( 0.000129) BitMap class : 0.000000 0.000000 0.000000 ( 0.000054)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Okay, I tend to agree with you about adding the method directly to the class. Namespace conflicts are likely to occur but I also think using this approach is OK if you're cautious about it: only adds methods that are actually extensions to the classes and you have control over the application environment.
Since integers are historically used to represent a string of bits, I still think that make senses to have bit related methods on Fixnum, but again, in a controlled environment.
Anyway, in a application of mine I would definitely use my latest example where I create a separate class using composition (and possible, delegation). Here is why and some counter-opinions on your arguments:
If performance is a problem then sometimes you need to sacrifice the design and choose a different approach and if it is really a problem it is probably a better idea not to use an object oriented language and choose another one that is faster (C, Go, Scala, whatever).
Yes, depending on how you implement the class you will be creating a lot of instances but you don't necessarily needs to create a instance for each method call. Just stop thinking about it as a wrapper and think of it as a actually behavioral class and you'll see that there will be only a few (if none) methods that will require you to return another instance of the same class. Take the String class, for example: it also creates a new instance of String when some methods are called (i.e. String#+) but it also has destructive methods that change the instance (i.e. String#<<).
Also, I think your example wasn't really appropriate to compare to my BitMap implementation, since your SpanishString seems more like a text decorator.
Another reason to use a separate class is that it's easy to change the representation of your bit string, using the power of duck typing. Assuming the variable @integer is now named @bitstring ;), consider the following:
Anyway, I don't think you shouldn't use module methods, but usually the module methods I create are to be called from objects outside the namespace to perform a certain action that uses classes/definitions from within the module. For example if you have a complex set of classes inside the namespace and you have a very common use case of the classes: instead of creating and setting up the instances for each time you need it, you'd wrap that on a module method and call that method.
About the performance issue, if you're really worried about optimization, here is benchmark of your module method implementation against mine:
And here are two results:
Both are fasts and pretty close to each other, even creating a new instance. That's because I'm using the Fixnum#[] operator instead of doing string conversions and transforming to array. So, make sure what to optimize ;)