Прежде всего, включите необходимые определения в такой вопрос.
hyp :: Floating a => a -> a -> a
hyp a b = sqrt $ a^2 + b^2
Теперь. Вы можете «встроить» функции в список в виде списка. Видимо, вы просто выбрали некоторых несчастных! round
имеет следующий вид:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :t round
round :: (Integral b, RealFrac a) => a -> b
Так, round c == c
имеет смысл, вы должны были бы иметь тип номера, который одновременно является экземпляром Integral
и RealFrac
. Другими словами, этот тип будет содержать дроби, но все его элементы будут целыми числами †. Ну, вы не можете иметь свой торт и съесть его тоже!
Эта проблема была бы гораздо более очевидной, как это часто бывает в Haskell, если бы вы написали какие-то подписи типа. Обманывая это, часто помогает просто выбрать простой примерный тип. Пожалуй, самое разумное, что может показаться:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] :: [Integer]
<interactive>:6:41:
No instance for (Floating Integer) arising from a use of ‘hyp’
In the expression: hyp a b
In an equation for ‘c’: c = hyp a b
In the expression:
[c |
a <- [1 .. 3], b <- [1 .. 4], let c = hyp a b, c == round c] ::
[Integer]
Итак, Integer
не работает, потому что вы пытаетесь сделать реальную арифметику, с этими квадратными корнями в hyp
. Это невозможно с Integer
, вам нужен тип Floating
, такой как Double
. Давайте попробуем, что:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] :: [Double]
<interactive>:8:55:
No instance for (Integral Double) arising from a use of ‘round’
In the second argument of ‘(==)’, namely ‘round c’
In the expression: c == round c
In a stmt of a list comprehension: c == round c
Хорошо, это происходит потому, что, как я сказал, round
всегда дает результаты интегрального типа. Тем не менее, вы всегда можете преобразовать такой интегральный тип в Double
снова:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == fromIntegral (round c)] :: [Double]
[5.0]
Обратите внимание, что это на самом деле не является хорошим решением, хотя вы действительно не хотите, чтобы результат с плавающей точкой, если вы идете уже проверить что элементы действительно цельные! Я бы рекомендовал в этом случае не просто оценить hyp
как таковой вообще. Лучше использовать это понимание:
Prelude> [ c | a <- [1..3], b <- [1..4], let c² = a^2 + b^2; c = round . sqrt $ fromIntegral c², c^2==c²] :: [Integer]
[5]
Один большой аргумент для этой версии является то, что он делает сравнение в Integer
, а не в Double
. Сравнение равенства с плавающей точкой - это то, что вам лучше всего избегать полностью, если вы можете помочь ему; в этом случае в основном безвреден, потому что интересная вещь - целое подмножество, которое на самом деле может быть представлено точно (в отличие от десятичных дробей, таких как 0.1
).Тем не менее вы получаете неправильные результаты таким образом: в частности, при достаточно больших числах с плавающей запятой c == fromInteger (round c)
будет всегда быть истинным, потому что над определенным порогом все значения являются целыми.
Prelude> [ c | a <- take 4 [1000000000000..], b <- take 5 [1000000000000..], let c = hyp a b, c == fromIntegral (round c)] :: [Float]
[1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12]
Но ни один из них не являются на самом деле правильные интегральные гипотенуз, как вы можете видеть, с версией, что делает сравнение в Integer
:
Prelude> [ c | a <- take 4 [1000000000000..], b <- take 5 [1000000000000..], let c² = a^2 + b^2; c = round . sqrt $ fromIntegral c², c^2==c²] :: [Integer]
[]
Строго говоря, это улучшенная версия также не безопасно, хотя - он не дает ложных срабатываний, но может не найти реальных пифагорейских троек, потому что шаги с плавающей точкой с потерями уже могут уничтожить равенство. Для того, чтобы сделать его реальным образом, вам нужно все-интеграл
intSqrt :: Integral a => a -> Maybe a
Это, вероятно, может сделать достаточно эффективно, принимая поплавок sqrt
в качестве начального значения для нескольких раундов псевдо Newton-Raphson в целочисленной арифметике.
† В принципе, функция round
может также иметь более расслабленную подпись.
round' :: (Num b, RealFrac a) => a -> b
round' = fromInteger . round
С этой версии, исходный код будет работать.
Я не знаю, как выглядит ваша 'hyp', но вы могли бы попробовать' c == fromIntegral (round c) 'в последнем утверждении понимания –
Так оно и было, спасибо! Я просто должен изучить роль Integral, прежде чем двигаться вперед. –