Еще один способ сделать это - использовать функцию logBase
с основанием 2
.
isPot :: (RealFrac b, Floating b) => b -> Bool
isPot = ((==) <*> (fromInteger . round)) . logBase 2
Prelude> isPot 2048
True
Prelude> isPot 1023
False
Prelude> isPot 1
True
Редактировать: OK, прежде чем оценивать код, давайте получим математику. за этим. Идея проста. Любое число, которое может быть выражено как степень двух, означает, что его логарифм по отношению к основанию 2 (logBase 2
) является целым числом.
Соответственно, мы будем использовать функцию logBase 2
. Если мы проверим его тип
Prelude> :t logBase 2
logBase 2 :: Floating a => a -> a
, мы увидим, что входной номер должен быть членом класса типа Floating
. Функция logBase 2
состоит из предыдущей функции;
(==) <*> (fromInteger . round)
Теперь, если мы забудем о аппликативном операторе <*>
, это можно перефразировать как
\n -> n == (fromInteger. round) n
Теперь, еслимы проверяем тип round
function
Prelude> :t round
round :: (RealFrac a, Integral b) => a -> b
и видим, что входные данные также должны быть членами класса RealFrac
, следовательно, ограничение (RealFrac b, Floating b) =>
в объявлении типа isPot
function.
Теперь, что касается аппликативного оператора <*>
, на самом деле это очень удобно, когда у вас есть двухпараметрическая функция, такая как (==)
, и вам нужно подать влево (первый параметр) с помощью x
и вправо с g x
. Другими словами f <*> g = \x -> f x (g x)
. Подпись типа <*>
:
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Я бы посоветовал вам прочитать Функторы, Аппликативные Функторы и Моноиды часть книги «Изучите вас на Хаскеле».