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?

4
  • @CarySwoveland Did you mean D[v3] as D(v3) did not work for me? Commented Jan 9, 2016 at 17:55
  • You could write 8*D[v3] + 4*D[v2] + 2*D[v1] + D[v0], where D = { true=>1, false=>0 } Commented Jan 9, 2016 at 18:20
  • 1
    @Wand, thanks. You're always so diplomatic. Aside: in your next avatar, how about you waving a magic wand? Commented Jan 9, 2016 at 18:21
  • 4
    @CarySwoveland Done! Commented Jan 9, 2016 at 18:32

4 Answers 4

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
Sign up to request clarification or add additional context in comments.

2 Comments

I lean toward using << as it is how we'd think of it in assembler.
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.
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

2 Comments

n * (2**exp) is equivalent to n << exp
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) }
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.

8 Comments

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.
You could put it in a Refinement and only activate the Refinement within the scope of your own code.
@SimoneCarletti I understand, that's why in the very beginning of the answer I've written "in this certain example".
Aside from this question, perhaps there's a case for to_i methods being added to Ruby's TrueClass and FalseClass.
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.
|
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

2 Comments

This is very insightful. While I understand I shouldn't be monkeypatching, it helps understand the language.
Rails Active Record Core Extensions are pretty useful set of monkey-patching.

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.