Почему моя функция gcd в haskell работает только тогда, когда она возвращает тип Writer [String] Int, а не Writer String Int? - PullRequest
2 голосов
/ 01 октября 2019

Следующий код не работает:

gcd' :: Int -> Int -> Writer String Int
gcd' a b 
 | b == 0 = do
     tell "gcd " ++ show a ++ " " ++ show b ++ " \n"
     return a
 | otherwise = do
     tell "gcd " ++ show a ++ " " ++ show b ++ " \n"
     gcd' b (a `mod` b)

Когда я изменил код на этот, он теперь работает:

gcd' :: Int -> Int -> Writer [String] Int
gcd' a b 
 | b == 0 = do
     tell ["gcd " ++ show a ++ " " ++ show b ++ " \n"]
     return a
 | otherwise = do
     tell ["gcd " ++ show a ++ " " ++ show b ++ " \n"]
     gcd' b (a `mod` b)

Хотя мне теперь пришлось бы объединить списокстрок.

Я не понимаю, почему мой оригинальный код не работает. Конечно, мой оригинальный код должен объединять строки каждого шага, чтобы получить общий журнал. Но вместо этого мне выдается следующее сообщение об ошибке:

gcdLogger.hs:6:6: error:
    • Couldn't match type ‘[]’
                     with ‘WriterT String Data.Functor.Identity.Identity’
      Expected type: WriterT String Data.Functor.Identity.Identity ()
        Actual type: [()]
    • In a stmt of a 'do' block:
        tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
      In the expression:
        do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
           return a
      In an equation for ‘gcd'’:
          gcd' a b
            | b == 0
            = do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
                 return a
            | otherwise
            = do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
                 gcd' b (a `mod` b)

1 Ответ

3 голосов
/ 01 октября 2019

Ваша первая попытка не работает, так как приложение функции имеет приоритет над операторами. Таким образом, он анализируется как:

(tell "gcd ") ++ (show a) ++ " " ++ (show b) ++ "\n "

Во второй попытке вы каждый раз переносите строки в одноэлементный список, поэтому вы пишете список String s (ну, все эти списки содержат одну строку, но это все равно не делает эти String с).

Вы можете решить проблему, добавив скобки здесь:

gcd' :: Int -> Int -> Writer String Int
gcd' a b = do
    tell <b>(</b>"gcd " ++ show a ++ " " ++ show b ++ " \n"<b>)</b>
    if b == 0 then
        return a
    else
        gcd' b (a `mod` b)

Например:

Prelude Control.Monad.Trans.Writer.CPS> runWriter (gcd' 15 5)
(5,"gcd 15 5 \ngcd 5 0 \n")

Обратите внимание, что для целей отладки вам удобнее использовать trace :: String -> a -> a вместо Writer, так как он будет лучше выполнять то, что вы хотите.

...