2012-01-18 2 views
3

Я хотел сделать простое среднее (среднее значение) функции в Haskell, поэтому я попытался следующее GHCI:Почему эта средняя функция в haskell не работает?

ghci> let avg xs = (sum xs)/(length xs) 

И выдает следующее сообщение об ошибке:

No instance for (Fractional Int) 
    arising from a use of `/' 
Possible fix: add an instance declaration for (Fractional Int) 
In the expression: (sum xs)/(length xs) 
In an equation for `avg': avg xs = (sum xs)/(length xs) 

Итак, я решил выполните следующие действия:

ghci> let a = (sum [1,2]) 
ghci> let b = (length [1,2]) 

Это все работает хорошо.

Итак, я попытался следующий

ghci> a/b 

И я получил следующее сообщение об ошибке:

Couldn't match expected type `Integer' with actual type `Int' 
In the second argument of `(/)', namely `b' 
In the expression: a/b 
In an equation for `it': it = a/b 

Так, в Haskell являются Integer и Int разные? - И если да, то как я могу заставить оригинальную функцию работать?

+0

Требуется больше 'fromIntegral'. =) –

ответ

11

Проблема заключается в том, что

length :: [a] -> Int 

но

(/) :: (Fractional a) => a -> a -> a 

Итак, когда вы говорите whatever/length xs, это не тип, потому что Int не является фракционный тип номера! Это то, что пытается сказать вам ошибка «Нет экземпляра для ...». Это определение будет работать:

GHCi> let avg xs = sum xs/fromIntegral (length xs) 

Здесь мы используем fromIntegral :: (Integral a, Num b) => a -> b преобразовать Int мы получаем от length в дробное число. Обратите внимание, что полученная функция будет работать только в списках Дробных чисел из-за этого (но, например, avg [1,2,3] все равно будет работать нормально).

Чтобы объяснить ошибки вы получаете, когда делать это «на части», это потому, что в let a = sum [1,2], элементы в списке являются Целые, поэтому их сумма является Integer, но в let b = length [1,2], результирующая длина является Int, согласно типу от length Я показал выше. Итак, когда вы делаете a/b, это не удается, даже если он даже понимает, что Int и Integer не являются экземплярами Fractional - поскольку (/) принимает два аргумента одного типа, он не может работать.

И да, Integer и Int отличаются друг от друга - Int является интегральным типом с фиксированной точностью, обычно размером с числовым словом, например long в C, тогда как Integer представляет собой произвольный прецизионный бигнам, способный хранить любое целое число; или, по крайней мере, любое целое число, которое будет соответствовать в оперативной памяти :)

Другим возможным подходом было бы определить, как avg xssum xs/genericLength xs, используя Data.List.genericLength, который работает с любым числовым типом, а не только Ints:

genericLength :: (Num b) => [a] -> b 

Для этого вам нужно будет import Data.List в GHCi. Еще один возможный подход (но один из которых приводит к другой функции вообще) заключается в использовании целочисленного деления: let avg xs = sum xs `div` length xs (примечание: a `div` b - это всего лишь div a b, этот синтаксический сахар работает для каждой функции).

+1

Поскольку вы определяете 'avg' с аргументом, то есть' let avg xs = ... ', ограничение мономорфизма не применяется, и дефолт не выполняется. Действительно, тип 'avg', как определено, равен' (Fractional a) => [a] -> a'. Однако, если вы попытаетесь использовать его, например, по умолчанию используется значение «Двойной». 'avg [1.5, 2]'. – hammar

+0

@ Хаммар: О, конечно; Я исправил свой ответ. Благодаря! – ehird

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