Haskell, изучающий осуществление, дает странные результаты

это - вопрос:

"Запишите функцию, которая вычисляет средний из списка, т.е. сумму всех элементов в списке, разделенном на его длину. (Вы, возможно, должны использовать функцию fromIntegral для преобразования длины списка от целого числа в число с плавающей точкой.)"

сначала я попробовал это:

mean :: [Double] -> Double
mean []     = 0
mean (x:xs) = (x + mean xs) / (1 + mean xs)

но это дает мне странные результаты, например, когда я использую его как этот:

mean [2,4,6]  

это дает мне результат: 1.41176
где это должно быть: 4

почему?

я попробовал другую вещь:

mean :: [Double] -> Double  
mean list = (summ list) / (count list)  
    where count []     = 0  
          count (x:xs) = 1 + count xs  
          summ []      = 0  
          summ (x:xs)  = x + summ xs

но у меня есть ошибка, когда я пытался загрузить файл в GHC.
ошибка:

parse error on input 'count'  
Failed, modules loaded: none

снова, что я делаю неправильно?

наконец, я попробовал этого (который успешно выполнился):

mean :: [Double] -> Double

count []     = 0
count (x:xs) = 1 + count xs
summ  []     = 0
summ (x:xs)  = x + summ xs
mean list    = summ list / count list

это совпадает с выше одного (с, 'где' ключевое слово), но это успешно выполняется только здесь, а не в выше одного.
почему?

большое спасибо.

p.s.
я узнаю из книги - Реальный мир, который Haskell и осуществление отсюда - (прокрутите его вниз :-))


благодаря Вам всем. это - странная вещь. второй пример также работает на меня также, когда я скопировал его отсюда и протестировал его. я не знаю, почему это вчера не работало на меня :-)

но я все еще не понимаю, почему первый не работает. я думаю, что это должно быть похожим на это


(2 + mean [4,6]) / (1 + mean [4,6])
(4 + mean [6  ]) / (1 + mean [  6])
(6 + mean [   ]) / (1 + mean [   ])      

таким образом, теперь это похоже на это


(6 +  0        )    / (1 +  0          )  -- last recursion
(4 + (6 + 0)   )    / (1 + (1 + 0)     )
(2 + (4 + (6 + 0))) / (1 + (1 + (1 + 0))

таким образом, теперь это должно быть: 12 / 3
не так ли?
что я не понимаю?
спасибо :-).

6
задан Guy Coder 15 December 2013 в 15:20
поделиться

7 ответов

  1. Вы получаете неправильные ответы при первой попытке, так как используете неправильную формулу. Мусор внутрь, мусор наружу. (Другие рассмотрели это.)

  2. Вероятно, Вы получаете ошибку при разборе, потому что в некоторых строках используются пробелы, а в других - табуляции. Или вы используете все табуляции, но с нестандартной шириной табуляции.

  3. Здесь не используется и не требуется отступ, поэтому вопрос с пробелами -v- табуляции не возникает.

5
ответ дан 8 December 2019 в 18:37
поделиться

Ваша первая попытка оценивается так:

6 / 1
4 + 6 / 1 + 6
2 + (10/7) / 1 + (10/7)

, что не то, что вы хотели.

Вторая попытка в порядке.

2
ответ дан 8 December 2019 в 18:37
поделиться

пусть будет среднее x = сумма (x) / изIntegral(length(x))

среднее [2.0, 4.0, 6.0]

Конечно, это должно быть улучшено (пустой список, работает для удвоений...)

.
0
ответ дан 8 December 2019 в 18:37
поделиться
[

]Haskell: Самый красивый императивный язык[

] [
import Control.Monad.ST
import Data.STRef 
import Control.Monad

mean xs = s / l  
    where (s,l) = runST $ do{
       acc <- newSTRef (0,0);
       forM_ xs $ \x -> do{
          modifySTRef acc $  \(a,b) -> (x+a,1+b)
       };
       readSTRef acc }
]
1
ответ дан 8 December 2019 в 18:37
поделиться

Правильно:

import Data.List (foldl')
mean :: Fractional a => [a] -> a
mean = uncurry (/) . foldl' (\(s, n) x -> (s + x, n + 1)) (0, 0)

mean [2,4,6] = uncurry (/) $ foldl' (...) (0, 0) [2,4,6]
             = uncurry (/) $ foldl' (...) (2, 1) [4,6]
             = uncurry (/) $ foldl' (...) (6, 2) [6]
             = uncurry (/) $ foldl' (...) (12, 3) []
             = uncurry (/) (12, 3)
             = 12 / 3
             = 4

Неправильно:

mean [] = 0
mean (x:xs) = (x + mean xs) / (1 + mean xs)

mean [6] = mean (6 : [])
         = (6 + mean []) / (1 + mean [])
         = (6 + 0) / (1 + 0)
         = 6
mean [4,6] = mean (4 : [6])
           = (4 + mean [6]) / (1 + mean [6])
           = (4 + 6) / (1 + 6)
           = 10/7
mean [2,4,6] = mean (2 : [4,6])
             = (2 + mean [4,6]) + (1 + mean [4,6])
             = (2 + 10/7) / (1 + 10/7)
             = 24/17
2
ответ дан 8 December 2019 в 18:37
поделиться

Предупреждение: непроверенный код впереди. В вашем определении означает , вы должны точно нести и бегущую сумму, и бегущую длину, и, как отмечали другие, вы этого не делаете. Должно сработать что-то вроде следующего:

mean0 sum len [] = sum / len
mean0 sum len (x:xs) = mean0 (sum+x) (len+1) xs

Это определение проходит вдоль двух аккумуляторов , по одному для текущей суммы и длины пробега, которые обновляются по мере того, как вы повторяетесь по списку. Когда у функции, наконец, заканчивается список для обработки (базовый случай), она просто делает необходимый расчет.

Чтобы использовать mean0, просто пишите

mean0 0 0 [2, 4, 6]

Как видите, это немного раздражает, предоставлять начальные значения для аккумуляторов, так что обычно предоставлять обертку как

mean xs = mean0 0 0 xs

Теперь, вы пишите mean [2, 4, 6] так, как вы хотели.

1
ответ дан 8 December 2019 в 18:37
поделиться

скажем, мы определяем

naivemean l = sum l / fromIntegral (length l)

, у него есть пара серьезных ограничений. Во-первых, определение не покрывает списки INT :

*Main> naivemean ([1] :: [Int])

<interactive>:1:0:
    No instance for (Fractional Int)
      arising from a use of `naivemean' at <interactive>:1:0-21
    Possible fix: add an instance declaration for (Fractional Int)
    In the expression: naivemean ([1] :: [Int])
    In the definition of `it': it = naivemean ([1] :: [Int])

Второе, он дует стек для больших списков:

*Main> naivemean [1..1000000]
*** Exception: stack overflow

также делает два пропуска, один для SUM и Один для длины , по поводу списка, когда будет делать один проход.

Мы можем исправить все три проблемы с

{-# LANGUAGE BangPatterns #-}

mean :: (Real a, Fractional b) => [a] -> b
mean = go (toRational 0) 0
  where
    go !s !l []     = fromRational s / fromIntegral l
    go  s  l (x:xs) = go (s+.x) (l+1) xs
    s +. x = s + toRational x

шаблонами взрыва Сила строгих оценок отмеченных параметров. Без челков, код выше, также взорвет стек при данном длинном списке, но по другой причине: ленивая оценка l , например, генерирует длинную нерешенную цепочку формы

0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + ...

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

Как новичок, из Фрации и , вероятно, тоже запутается . Рассмотрим тип функции разделения:

*Main> :t (/)
(/) :: (Fractional a) => a -> a -> a

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

*Main> (1::Int) / (2::Int)

<interactive>:1:0:
    No instance for (Fractional Int)
      arising from a use of `/' at <interactive>:1:0-18
    Possible fix: add an instance declaration for (Fractional Int)
    In the expression: (1 :: Int) / (2 :: Int)
    In the definition of `it': it = (1 :: Int) / (2 :: Int)

Определение означает должен покрыть все [INT] , [FLOAT] и [Двойной] [ДВИЖЕНИЕ] , но без рациональных битов (и без аннотации типа), тип выведенного в означает

*Main> :t mean
mean :: [Double] -> Double

из-за разделения по длине списка Отказ

Оказывается, INT , , , , и Двойной представляют собой экземпляры типекласс Real , и любой реальный может быть преобразован в Rational

*Main> :t toRational
toRational :: (Real a) => a -> Rational

и Rational , может быть преобразован в диактива :

*Main> :t fromRational
fromRational :: (Fractional a) => Rational -> a

Наконец, для больших списков также есть вероятность того, что мы могли бы Переполнение машины двойной, но Rational дает нам произвольную точность.

Если у вас есть фон на языках, таких как C или Java, который автоматически продвигает типы для обработки этих случаев, вы обнаружите, что эта особая негибкость системы типа Haskell сбивает с толку и разочарованию. Боюсь, тебе просто нужно научиться справляться с этим.

Сделав все это, теперь мы можем

*Main> mean ([1..1000000] :: [Int])
500000.5

или

*Main> mean ([1..1000000] :: [Double])
500000.5
2
ответ дан 8 December 2019 в 18:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: