Вопрос

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

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 , но я понятия не имею, как, или даже если это правильно? Есть идеи?

Это было полезно?

Решение

  

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

Почему это? Если вы хотите вычислить среднее значение для целого числа целых чисел, вам придется в какой-то момент разделить их, поэтому вам придется преобразовать их из целых чисел в поддерживаемый по вашему выбору тип поддержки. При внимательном рассмотрении класса Num (: i Num в ghci) обнаружена одна проблема с типом avg : Num недостаточно методов & # 8212; в основном достаточно сложить, умножить и вычесть. Нет никакой гарантии, что число, которое я даю 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 = > , который вызывает fromInteger для любого типа, которым он оказался, но выражение вроде 1/2 имеет более конкретный тип Fractional a = & GT; , который вы можете увидеть, если спросите тип этого выражения вместо его распечатки.

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

Другие советы

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

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

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

fromRational ((sum xs) % (leng xs))

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

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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top