Короче говоря : используя mod
, floor
и (**)
все одновременно, вы сильно ограничиваете тип p
, и Haskell не может найти числовой набрать для звонка prime
.
Основная проблема здесь заключается в итерации вашего списка:
[2..(floor(p**0.5))]
Здесь вы вызываете p ** 0.5
, но поскольку (**)
имеет тип (**) :: Floating a => a -> a -> a
, это означает, что p
должен быть экземпляром типа, который экземпляр класса типов Floating
, например Float
. Я думаю, вы этого не хотите.
Ваш floor :: (RealFrac a, Integral b) => a -> b
даже усугубляет ситуацию, поскольку теперь p
также должен иметь тип, который является экземпляром класса типов RealFrac
.
С другой стороны, вы используете mod :: Integral a => a -> a -> a
, так что это означает, что ваш p
должен быть Floating
, а также Integral
, которые скорее два дизъюнктивных набора: хотя, строго говоря, мы можем определить такой тип, довольно странно, что число одновременно равно Integral
и Floating
в одном и том же типе. Float
- это, например, число Floating
, но не Integral
, а Int
- это Integral
, но не тип Floating
.
Мы должны найти способ ослабить ограничения, наложенные на p
. Так как обычно числа, отличные от Integral
, вообще не являются простыми числами, нам лучше выбросить floor
и (**)
. Однако оптимизация для итерации до квадратного корня из p
является хорошей идеей, но нам нужно будет найти другие средства для обеспечения этого.
Один из способов сделать это - использовать takeWhile :: (a -> Bool) -> [a] -> [a]
, где мы берем элементы, пока квадрат чисел не станет больше p
, поэтому мы можем переписать [2..(floor(p**0.5))]
до:
takeWhile (\x -> x * x <= p) [2..]
Мы даже можем работать только с нечетными элементами и 2
, записав его как:
takeWhile (\x -> x * x <= p) (2:[3, 5..])
Если мы проверим это с p
, например, равным 99
, мы получим:
Prelude> takeWhile (\x -> x * x <= 99) (2:[3, 5..])
[2,3,5,7,9]
Если мы подключим это, мы ослабим тип:
prime p = if p == 1 then False else if p == 2 then True else if maximum ([if p `mod` x == 0 then x else -1 | x <- takeWhile (\x -> x * x <= p) (2:[3, 5..])]) > 0 then False else True
мы на самом деле достаточно расслабились:
Prelude> :t prime
prime :: Integral a => a -> Bool
и мы получаем:
Prelude> prime 23
True
Но код очень безобразный и скорее un-Haskell . Прежде всего, вы здесь используете maximum
как трюк, чтобы проверить, удовлетворяют ли все элементы предикату. Но нет смысла делать это таким образом: с того момента, как один из элементов делится, мы знаем, что число не простое. Таким образом, мы можем лучше использовать функцию all :: (a -> Bool) -> [a] -> Bool
. Кроме того, условия обычно проверяются с помощью сопоставления с шаблоном и защитных элементов, поэтому мы можем записать это как:
prime :: Integral a => a -> Bool
prime n | n < 2 = False
| otherwise = all ((/=) 0 . mod n) divisors
where divisors = takeWhile (\x -> x * x <= n) (2:[3, 5..])