Факториальная функция в IO монаде haskell

Я делаю факториальную функцию в haskell монады IO. Я последовал примеру mod3, чтобы выполнить функцию факториала с монадой ввода-вывода. Я не могу понять, почему мой код неверен. Я хочу посмотреть, если n>=1, а затем сделать факториал для n. Наконец добавление к (n,r2) и возврат. Кто-нибудь может помочь понять это?

while :: IO Bool -> IO () -> IO ()
while test body =
  do b <- test
     if b
       then do {body ; while test body}  -- same-line syntax for do
       else return ()

-- remainder when integer-dividing n by 3
mod3 :: Integer -> IO Integer
mod3 n = do r <- newIORef n
            while
              (do {v <- readIORef r; return (v >= 3)})
              (do {v <- readIORef r; writeIORef r (v-3)})
            readIORef r          

-- ghci> fact 4
-- (4,24)
fact :: Integer -> IO (Integer, Integer)
fact n = do r2 <- newIORef n
            while 
            (do {v2 <- readIORef r2; return (v2 >= 1)})
            (do {v2 <- readIORef r2; writeIORef r2 (v2*fact(v2-1))})
            readIORef (n,r2) 

3 ответа

В этой части вашего кода:

readIORef (n,r2) 

(n,r2) это не IORef. r2 это IORef, так что вы это законно:

readIORef r2

Как насчет того, чтобы попробовать это:

  1. прочитайте IORef r2 для получения значения
  2. вернуть пару (n, ...) где... это значение, которое вы получили в шаге 1

Во-первых, отступ в аргументах whileВ противном случае они являются новыми заявлениями.

fact :: Integer -> IO (Integer, Integer)
fact n = do r2 <- newIORef n
            while 
              (do {v2 <- readIORef r2; return (v2 >= 1)})
              (do {v2 <- readIORef r2; writeIORef r2 (v2*fact(v2-1))})
            readIORef (n,r2) 

Во-вторых, внешний вид пока примерно соответствует императивному коду:

while r2 >= 1 :
  r2 = r2 * fact(r2-1)

Это не имеет смысла: почему рекурсивный вызов? Конечно, вы знаете, как написать факториал в императивном стиле на обычном императивном языке; Начните с этого и конвертируйте его в Haskell.

Я вижу две проблемы, которые мешают компиляции. Во-первых, это fact (v2-1) имеет тип IO (Integer, Integer), но вы пытаетесь умножить его на v2 :: Integer, Вам нужно последовательность fact (v2-1) как вы последовательность readIORef r2умножьте на второй компонент пары.

Во-вторых, вы злоупотребляете readIORef, Вы можете увидеть его подпись здесь. Требуется IORef a, а не пара.

Другие вопросы по тегам