Haskell, разделяющий число - PullRequest
4 голосов
/ 30 ноября 2009

У меня есть проблема, о которой я хочу узнать больше, и как ее избежать. У меня есть этот код

len :: (Num r ) => [a] -> r
len [] = 0
len xs = 1 + len ( tail xs )

avg :: (Num t) => [t] -> Double
avg xs = ( sum xs ) / ( len xs )

Что приводит к следующей ошибке

len.hs:6:9:
    Couldn't match expected type `Double' against inferred type `t'
      `t' is a rigid type variable bound by
          the type signature for `avg' at len.hs:5:12
    In the expression: (sum xs) / (len xs)
    In the definition of `avg': avg xs = (sum xs) / (len xs)

Теперь я знаю, что эта ошибка (благодаря irc.freenode.net # haskell) является результатом функции деления

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

Однако я не знаю, что делать. Моя сигнатура функции avg не должна иметь ничего общего с особенностями операторов деления (требуется класс типов Fractional). Итак, я остаюсь, думая, что правильный путь для преодоления этого - приведение к типу, который подразумевает класс типов Fractional, но я понятия не имею, как, или даже если это правильно? Есть идеи?

Ответы [ 4 ]

7 голосов
/ 30 ноября 2009

Моя avg сигнатура функции не должна иметь ничего общего с причудами оператора деления

Почему это? Если вы хотите вычислить среднее значение для целого числа целых чисел, вам придется в какой-то момент разделить их, поэтому вам придется преобразовать их из целых чисел в поддерживаемый по вашему выбору тип поддержки. При внимательном рассмотрении класса Num (:i Num в ghci) обнаруживается одна проблема с типом avg: Num не имеет достаточно методов - в основном достаточно для сложения, умножения и вычитания. Нет никакой гарантии, что число, которое я даю avg, можно вообще преобразовать в Double.

Если вы введете нетипизированную функцию для вычисления среднего, Haskell ответит самым общим из возможных типов:

Prelude List> :type \x -> sum x / genericLength x
\x -> sum x / genericLength x :: (Fractional a) => [a] -> a

Так что это правильный тип avg.

Вы можете заметить, что avg [1,2,3 :: Integer] выдает ошибку типа. Вы можете обойти это, передав аргумент сначала toRational или fromIntegral, которые используют экземпляры Real и Integral для Integer соответственно.


Относительно выражения sum [1,2,3] / len [1,2,3]: Это правда, что буквенное число, такое как 1, имеет тип Num a => a, что вызывает fromInteger для любого типа, которым оно окажется, но выражение, подобное 1/2, имеет более конкретный тип Fractional a => a, который вы можете увидеть, если спросите тип этого выражения вместо его распечатки.

Что-то, что могло бы быть полезным, это :set -Wall в ghci, который включает много предупреждений всякий раз, когда для вас выбран тип по умолчанию, давая понять, что наиболее общий тип может больше не быть правильным.

5 голосов
/ 30 ноября 2009

Вы чрезмерно ограничиваете тип avg. Используйте более общую версию, avg :: (Fractional a) => [a] -> a

1 голос
/ 30 ноября 2009

хм, на самом деле проблема в том, что если это целочисленный тип, который вы хотите привести к дробному типу, но если это дробный тип, вы хотите оставить его в покое.

Попробуйте это

fromRational ((sum xs) % (leng xs))
0 голосов
/ 09 октября 2010

Я столкнулся с этой проблемой. Лучшее, что мне удалось сделать, - это две функции для усреднения: одна для интегралов и одна для дробных:

avgInt :: (Integral i, Fractional f) => [i] -> f
avgInt xs = fromIntegral (sum xs) / fromIntegral (length xs)

avgFrac :: (Fractional f) => [f] -> f
avgFrac xs = sum xs / fromIntegral (length xs)
...