Shawn - 11 months ago 39

Ruby Question

I have this code to return

`true`

`num`

`def is_power_of_two?(num)`

result = num.inject(0) {|n1, n2| n2 ** n1}

if result == num

true

else

false

end

end

p is_power_of_two?(16)

I keep getting an error though. How could I fix and simplify this code?

Answer Source

**Code**

```
R = /
\A # match beginning of string ("anchor")
10* # match 1 followed by zero or more zeroes
\z # match end of string ("anchor")
/x # free-spacing regex definition mode
def po2?(n)
!!(n.to_s(2) =~ R)
end
```

**Examples**

```
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
po2? -7 #=> false
```

**Explanation**

Fixnum#to_s provides the string representation of an integer (the receiver) for a given base. The method's argument, which defaults to 10, is the base. For example:

```
16.to_s #=> "16"
16.to_s(8) #=> "20"
16.to_s(16) #=> "10"
15.to_s(16) #=> "f"
```

It's base 2 we're interested in. For powers of 2:

```
1.to_s(2) #=> "1"
2.to_s(2) #=> "10"
4.to_s(2) #=> "100"
8.to_s(2) #=> "1000"
16.to_s(2) #=> "10000"
```

For a few natural numbers that are are not powers of 2:

```
3.to_s(2) #=> "11"
5.to_s(2) #=> "101"
11.to_s(2) #=> "1011"
```

We therefore wish to match strings that begin with `1`

and are followed by zero or more zeroes. The regex above (`R`

) does that.

`!!`

is a trick to convert "falsy" (`false`

or `nil`

) or "truthy" (everything that's not falsy) values to `false`

and `true`

respectively. Suppose, for example, `n.to_s(2) =~ R`

returned `0`

, meaning there is a match starting at offset zero. Then:

```
!!0 #=> !(!0) => !(false) => true
```

Similarly, if there is no match:

```
!!nil #=> !(!nil) => !(true) => false
```

`!!`

is not necessary if you are only concerned with whether the return value is truthy or falsy.

**Another Way**

This uses Fixnum#bit_length and Fixnum#[]:

```
def po2?(n)
m = n.bit_length-1
n[m] == 1 and m.times.all? { |i| n[i].zero? }
end
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
po2? -7 #=> false
```