Добавление некоторых скобок здесь и там устраняет проблему:
factorise (n, ps)
| mod n (head ps) /= 0 = div n (head ps) : factorise (div n (head ps), ps)
| otherwise = factorise (n, tail ps)
Круглые скобки используются для группировки в Haskell. mod n (head ps)
является приложением mod
к двум аргументам, n
и head ps
; без запятых mod n head ps
- это применение mod
к трем аргументам, n
, head
и ps`, и это просто не проверяет тип.
Действительно, после исправления ошибки все типы успешно выводятся, как
primes :: Integral a => [a]
sieve :: Integral a => [a] -> [a]
factorise :: Integral a => (a, [a]) -> [a]
Теперь вы можете специализировать их, если вы так * wi sh, как
primes :: [Integer]
sieve :: [Integer] -> [Integer]
factorise :: (a ~ Integer) => (a, [a]) -> [a]
(последний способ написания типа для factorise
требуется расширение GADTs
).
Для вашего исходного кода с включенной сигнатурой типа
factorise :: Integral a => (a, [a]) -> [a]
factorise (n,ps)
| mod n head ps /= 0 = div n head ps : factorise (div n head ps, ps)
| otherwise = factorise (n,tail ps)
сообщение об ошибке по сути то же самое. Он говорит «Не удалось вывести (a ~ ([a] -> a))
», но все же показывает, что, насколько видит компилятор, тип a
также должен быть одновременно [a] -> a
(поэтому он также должен быть a ~ [a] -> ([a] -> a)
, a ~ [[a] -> a] -> ([a] -> ([a] -> a))
, et c. et c. (отсюда и «бесконечная» ссылка на тип в первом сообщении об ошибке)).
Если бы это был действительно тип, который вы намеревались, его можно было бы сделать законным, назвав его и делая его явно рекурсивным, как
data T a = MkT ([T a] -> T a)
Это допускается . Haskell вполне может иметь рекурсивные типы. В конце концов, простой тип list является рекурсивным, как если бы он был определен как
data [] a = [] | a : ([] a)
data L a = Nil | Cons a (L a)
. Я оставлю решение проблем эффективности в factorise
на потом. С sieve
, хотя это довольно просто: его проблема main в том, что он производит только одно простое число за раз, тогда как он вполне способен производить больше, намного больше, чем на каждом шаге.
Можете ли вы найти способ, как?
Ваша factorise
функция представляет собой превосходную отрисовку простого алгоритма факторизации деления проб (за исключением нескольких простых ошибок). Один из способов улучшить краткость (и, следовательно, правильность!) - ввести временные переменные с let
(или, лучше, where
, чтобы иметь возможность использовать их и в охранниках), как в
factorise (n, ps)
| mod n f /= 0 = -- is the test correct?
v : factorise (v, ps) -- is it really v : ... ?
| otherwise = factorise (n, tail ps)
where
f = head ps
v = div n f
... кроме как никогда! Вы должны включить дополнительный тест, чтобы прекратить составление списка основных факторов.
Можете ли вы найти способ как? :)