2016-11-08 2 views
1

Я учу себя Haskell и играю со списком. Я написал этот список понимание:Haskell: Функции в списках понятий

[ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] 

Я надеялся, что он будет производить список значений с, где с представляет собой целое число (с == круглый с), который будет только 5, но он не компилируется. Играя еще немного, я обнаружил, что не могу встроить какие-либо функции в понимание списка, я уверен, что есть способ, я просто не знаю, как это сделать.

Вот код ошибки:

<interactive>:158:1: error: 
    • Ambiguous type variable ‘t0’ arising from a use of ‘it’ 
     prevents the constraint ‘(Floating t0)’ from being solved. 
     Probable fix: use a type annotation to specify what ‘t0’ should be. 
     These potential instances exist: 
     instance Floating Double -- Defined in ‘GHC.Float’ 
     instance Floating Float -- Defined in ‘GHC.Float’ 
    • In the first argument of ‘print’, namely ‘it’ 
     In a stmt of an interactive GHCi command: print it 

Спасибо!

+0

Я не знаю, как выглядит ваша 'hyp', но вы могли бы попробовать' c == fromIntegral (round c) 'в последнем утверждении понимания –

+0

Так оно и было, спасибо! Я просто должен изучить роль Integral, прежде чем двигаться вперед. –

ответ

2

Прежде всего, включите необходимые определения в такой вопрос.

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 

С этой версии, исходный код будет работать.

Смежные вопросы