2010-07-30 3 views
2

Я хочу написать функцию для вычисления среднего, используя государственную монаду в Haskell это код, который я написал, насколькосостояния монады Haskell

import Control.Monad.State 
type MyState = (Double,Double) 
media s (a,n)= ((a*n+s)/(n+1),n+1) 

getAverage:: Double ->State MyState s1-> Double 
getAverage s c=get >>= \s0 -> let (x,s1) =media s s0 
      in put s1 >> return x 

Я получил эту ошибку при компиляции в GHCI, и я застрял там вы можете помочь мне понять, что не так, спасибо заранее

+4

Вы должны вставить ошибку. –

+0

Что вы вводите в свою командную строку для запуска этой функции 'getAverage'? –

ответ

6

код вы предоставили дает эту ошибку:

Couldn't match expected type `Double' 
     against inferred type `m Double' 
In the expression: 
     get >>= \ s0 -> let (x, s1) = ... in put s1 >> return x 
In the definition of `getAverage': 
    getAverage s c = get >>= \ s0 -> let ... in put s1 >> return x 

Все это означает, что тип, полученный в результате выражения («выводимый»), не согласуется с сигнатурой типа («ожидается»). В этом случае getAverage работает в монаде State, поэтому это неправильная подпись типа, поскольку она не может оценить немонодический тип.

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

  • getAverage имеет неиспользуемый параметр, который предположительно значение в State монады, которая на самом деле не имеет смысла в любом случае.
  • Использование обозначения do обычно понятнее, чем использование (>>=) и лямбда, особенно для чего-то вроде State.
  • Отступ второй линии запутан, так как in идет с let, то есть внутри лямбда.

внесении этих изменений мы имеем это:

getAverage s = do 
    s0 <- get 
    let (x, s1) = media s s0 
    put s1 
    return x 

... что делает его легче обнаружить следующую ошибку: второй аргумент media является 2-кортеж, и s1 только один номер, но вы пытаетесь использовать оба значения для состояния. Вероятно, вам нужно было установить состояние на (x, s1), но вернуться только x.

getAverage s = do 
    s0 <- get 
    let (x,s1) = media s s0 
    put (x,s1) 
    return x 

Это компилируется нормально, но все еще нуждается в уборке:

  • media необходимо обновить всю государственную ценность, так что вместо get тин и put тин, просто используйте функцию modify.
  • Возвращаемое значение является первой частью значения состояния, поэтому только fmap ing fst более get более прост.

Так что теперь у нас есть что-то вроде этого:

media :: Double -> MyState -> MyState 
media s (a, n) = ((a * n + s)/(n + 1), n + 1) 

getAverage:: Double -> State MyState Double 
getAverage s = do 
    modify (media s) 
    fmap fst get 

Мы можем также отметить, что getAverage вроде делать две разные вещи, и разделить его на отдельные функции:

updateAverage:: Double -> State MyState() 
updateAverage s = modify (media s) 

currentAverage :: State MyState Double 
currentAverage = fmap fst get 

getAverage:: Double -> State MyState Double 
getAverage s = updateAverage s >> currentAverage 

Редактировать: И так как я забыл о незначительной детали, фактически получив результаты обратно из монады, заменив updateAverage на getAverage в функции getAverages Travis Brown позволит работать над моим кодом выше.

+0

@camccann - как я могу запустить это из командной строки? Я загрузил его в ghci, но не знаю, каким должен быть вызов getAverage –

+0

@SamHeather: вам нужно использовать 'runState' или аналогичную функцию, например. 'runState (getAverage 1) (2, 3)'. См. Документы для государственной монады здесь: http://hackage.haskell.org/package/mtl-2.2.0.1/docs/Control-Monad-State-Lazy.html Если у вас есть вопросы, выходящие за рамки этого, вы, вероятно, будете иметь больше удачи, отправляя его как новый вопрос. –

3

Примечание: ответ от компании camcann лучше, чем у меня, но мой принимает несколько иной подход и дает пример того, как оценить государственную монаду, поэтому я оставляю ее здесь для справки.


Мы можем начать пытаться выяснить эту проблему путем удаления сигнатуры типа для getAverage и аргумента (c), который не появляется в функции:

getAverage s=get >>= \s0 -> let (x,s1) =media s s0 
      in put s1 >> return x 

Это еще не компиляция, потому что мы пытаемся найти put что-то, что не имеет подходящего типа: s1 - это Double, а не MyState. Это легко поправимо:

getAverage s=get >>= \s0 -> let [email protected](x,_) =media s s0 
      in put s1 >> return x 

Мы могли бы также оставить let шаблон без изменений и просто сказать put (x,s1): Я делаю это так, вместо того, чтобы наша s1 имеет тот же тип, как s0.

Это скомпилируется, поэтому теперь мы можем исправить подпись типа. Если мы спросим GHCI для данного типа, он возвращает следующее:

getAverage :: (Fractional t, MonadState (t, t) m) => t -> m t 

Double является экземпляром Fractional и State MyState является экземпляром MonadState (Double, Double), поэтому мы можем использовать что-то очень похожее на исходный тип для getAverage:

getAverage :: Double -> State MyState Double 

Эта функция не действительно «получить» в среднем: он обновляет его после добавления нового значения, так что давайте переименовать его соответствующим образом:

updateAverage :: Double -> State MyState Double 
updateAverage s=get >>= \s0 -> let [email protected](x,_) =media s s0 
      in put s1 >> return x 

Теперь мы можем определить getAverages функцию, которая принимает список Double с, проходит через их updateAverage, и возвращает список промежуточных средних на каждом шаге:

getAverages :: [Double] -> [Double] 
getAverages ss = evalState (mapM updateAverage ss) (0, 0) 

Это делает то, что мы ожидали бы:

*Main> getAverages [1..10] 
[1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5] 

Обратите внимание, что сделать что-нибудь полезное с State монады вы всегда будете использовать evalState (или тесно связаны runState и execState).

+2

В стороне, которая, как я думал, не помогла бы допрашивающему, но вам понравится: Обратите внимание, что управление потоком не зависит от значений состояния? Это контрольный знак, что это * не нуждается в монаде *, и поэтому ваша версия может быть переписана как 'updateAverage s = modify (media s) *> (fst <$> get)'. Аналогично, 'mapM' может быть обобщен как' traverse', что делает то же самое для любого 'Applicative' через любой' Traversable'. –