0

I have four boolean variables v0, v1, v2, v3 and want to get the integer expressed by them, taking the v's as binary digits, and their values false as 0 and true as 1, in other words:

8 * v3 + 4 * v2 + 2 * v1 + v0

What is the best way to cast them to such integers? Can this be done directly in a vector?

sawa
  • 165,429
  • 45
  • 277
  • 381
Diego-MX
  • 2,279
  • 2
  • 20
  • 35

4 Answers4

5

Just iterate. No need of power operation.

[false, true,  false, true] .inject(0){|n, b| n * 2 + (b ? 1 : 0)} # => 5
[false, false, false, false].inject(0){|n, b| n * 2 + (b ? 1 : 0)} # => 0
[false, false, false, true] .inject(0){|n, b| n * 2 + (b ? 1 : 0)} # => 1
[true,  false, false, true] .inject(0){|n, b| n * 2 + (b ? 1 : 0)} # => 9
sawa
  • 165,429
  • 45
  • 277
  • 381
  • 1
    I lean toward using `<<` as it is how we'd think of it in assembler. – the Tin Man Jan 09 '16 at 21:36
  • 1
    This is one of the perks of learning a new language. It is hard to understand just by looking at it, even while knowing what it does and being so short (as a newbie of course). But then a quick Google on `inspect` and revisiting blocks and then it makes Ruby much richer. – Diego-MX Jan 10 '16 at 13:31
3

Just create a custom method:

def bool_to_int(bool)
  bool ? 1 : 0
end

8*bool_to_int(v3) + 4*bool_to_int(v2) + 2*bool_to_int(v1) + bool_to_int(v0)

You can of course use an array and apply the function call to all the values in the list.

ary = [false, true, true, false]

exp = 0
ary.inject(0) do |total, value|
  total += bool_to_int(value) * (2**exp)
  exp += 1
  total
end

This is more concise. The first item in the array is the exponent, the second is the sum.

ary = [false, true, true, false]
ary.inject([0,0]) do |(exp, total), value|
  [exp + 1, total + bool_to_int(value) * (2**exp)]
end

As pointed out in the comments, you can also use <<

ary = [false, true, true, false]
ary.inject([0,0]) do |(exp, total), value|
  [exp + 1, total + (bool_to_int(value) << exp)]
end
Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
  • 2
    `n * (2**exp)` is equivalent to `n << exp` – Stefan Jan 09 '16 at 17:26
  • 1
    You could also use `each_with_index` to avoid the array: `ary.each_with_index.inject(0) { |total, (value, exp)| total + (bool_to_int(value) << exp) }` – Stefan Jan 09 '16 at 17:36
1

In this certain example you could add to_i method directly to true and false:

def false.to_i
  0
end

def true.to_i
  1
end

def int_from_boolean_array(array)
  sum = 0  
  array.each_with_index do |el, index|
    sum += el.to_i * (2**index)
  end
  sum
end

int_from_boolean_array([false, true, false, true])

It works because true (same for false) is just simple object in ruby and thus you could extend it. Also you could write the same in slightly different way:

class TrueClass
  def to_i
    1
  end
end

class FalseClass
  def to_i
    0
  end
end

First approach works because there is always only one instance of TrueClass and FalseClass in the system.

hedgesky
  • 3,271
  • 1
  • 21
  • 36
  • It's not a good idea to monkey patch low-level classes such as TrueClass, without mention you are introducing a common cast method that can definitely cause a lot of side effects in many libraries. Just think about the effect it will have on duck typing (`respond_to?`) usage. This is a very terrible idea. – Simone Carletti Jan 09 '16 at 17:38
  • 1
    You could put it in a Refinement and only activate the Refinement within the scope of your own code. – Jörg W Mittag Jan 09 '16 at 17:45
  • @SimoneCarletti I understand, that's why in the very beginning of the answer I've written "in this certain example". – hedgesky Jan 09 '16 at 17:50
  • 1
    Aside from this question, perhaps there's a case for `to_i` methods being added to Ruby's `TrueClass` and `FalseClass`. – Cary Swoveland Jan 09 '16 at 18:19
  • The problem with that is False/false is not 0, it's more equivalent to nil. And 0 is more akin to true than it is false. For specific bit-twiddling code it'd be OK, but I can't see it part of the language itself because it'd cause havoc in the minds of those who are first encountering it. – the Tin Man Jan 09 '16 at 21:41
  • This was my favorite answer because it seemed more natural. After reading the comments -however- I understand the objection and will be more careful when using it. – Diego-MX Jan 09 '16 at 22:26
  • @theTinMan Ruby's `pack/unpack` methods (especially big- vs little-endian) initially caused havoc in my mind, now they just bring on headaches. – Cary Swoveland Jan 10 '16 at 07:36
  • I found it a lot easier to deal with low-level bit operations when I was writing assembly code, probably because everything is at that level. Dealing with anything from C and above ruined my ability to think in those terms. – the Tin Man Jan 10 '16 at 22:57
1

You can monkey-patch TrueClass and FalseClass to respond to * and + methods. Here is a working solution largely based on discussion in this topic - In Ruby, how does coerce() actually work?

# Changes to `TrueClass` and `FalseClass`
class TrueClass
    def *(i)
        i * 1
    end
    def +(i)
        i + 1
    end
    def coerce(something)
        [self, something]
    end
end

class FalseClass
    def *(i)
        i * 0
    end
    def +(i)
        i + 0
    end
    def coerce(something)
        [self,something]
    end
end

# Sample Runs

v3,v2,v1,v0 = true,false,true,true
p v3*8 + v2*4 + v1*2 + v0
#=> 11
p 8*v3 + 4*v2 + 2*v1 + v0
#=> 11
p 8*true + 4*false + 2*false + true
#=> 9
Community
  • 1
  • 1
Wand Maker
  • 18,476
  • 8
  • 53
  • 87
  • This is very insightful. While I understand I shouldn't be monkeypatching, it helps understand the language. – Diego-MX Jan 10 '16 at 13:25
  • 1
    Rails [Active Record Core Extensions](http://edgeguides.rubyonrails.org/active_support_core_extensions.html) are pretty useful set of monkey-patching. – Wand Maker Jan 10 '16 at 13:27