如何在 Haskell 中将 main :: IO () 重写为由一个 main :: IO() 组成的两个函数

Posted

技术标签:

【中文标题】如何在 Haskell 中将 main :: IO () 重写为由一个 main :: IO() 组成的两个函数【英文标题】:How to rewrite in Haskell a main :: IO () to two functions consisting of one main :: IO() 【发布时间】:2019-08-26 02:36:46 【问题描述】:

我想通过拆分一些东西来使它成为一个更简单的主要功能。但是当我添加一个新函数 maakNieuwSpel 我得到错误

 maakNieuwSpel :: Int -> String -> IO ()
 maakNieuwSpel aantal firstName= do let rijTegels = volleRijTegels
                                    let spelers = deSpelers aantal firstName 
                                    w <- spelSpelen rijTegels spelers 0
                                    putStrLn (w ++ "dit is waar ik mee bezig was in functie nieuwSpel")

当我用

在 main 中调用它时
    eindstand <- maakNieuwSpel (digitToInt aantalTegenspelers) firstName

它给出了错误。

我尝试并想要实现的另一件事是通过放置 where 函数来重写 main 中的 let 函数。 我的想法是这样的

    eindstand <- spelSpelen rijTegels spelers 0 
            where rijTegels = volleRijTegels
                   spelers = deSpelers (digitToInt aantalTegenspelers) firstName  

然后它说“输入‘=’时出现解析错误,也许你需要在‘do’块中添加一个‘let’?”

这是我的代码的一部分。我想这会让你对我在做什么有足够的了解。

 data Dobbelsteen = Steen Char -- now its values can be specified
                deriving (Show, Eq)  
 data Tegel = Teg Int Int 
          deriving (Show, Eq) 
 data Speler = Spel Tactiek Spelersstapel String
 type Tactiek = ([Dobbelsteen]  -> [Dobbelsteen] -> IO [Dobbelsteen], [Dobbelsteen]  -> Int -> IO Bool)
  type Spelersstapel = [Tegel]



 main :: IO ()  
 main = do putStrLn ("Hoi! Je speelt regenwormen. Wat is je naam?")
           firstName <- getLine  
           putStrLn ("Beste "++ firstName ++ ", je speelt tegen twee computertegenstanders. Ze heten Lap en Top.")
           putStr "Tegen hoeveel computertegenstanders wil u spelen? (Dit spel is voor 2 tot 8 personen) "
           aantalTegenspelers <- getChar
           let rijTegels = volleRijTegels
           let spelers = deSpelers (digitToInt aantalTegenspelers) firstName 
           eindstand <- -maakNieuwSpel (digitToInt aantalTegenspelers) firstName- spelSpelen rijTegels spelers 0
           putStrLn ("Het spel is afgelopen. De eindstand is " ++ eindstand)


spelSpelen :: [Tegel]  -> [Speler]  -> Int -> IO String -- de RijTegels, de spelers en degene die gaat spelen (int)
spelSpelen []        spelers spelerNr = do return (show (bepaalEindStand spelers))
spelSpelen rijTegels spelers spelerNr = do let Spel tactiek stapel naam = (spelers !! spelerNr)
                                           putStrLn ("Deze speler is nu aan de beurt: " ++ naam)
                                           score <- beurt tactiek (\x -> elem x (bepaalGeldigeScore spelers naam rijTegels))
                                           let rijTegelsNieuw = pakTegelAlsMogelijk rijTegels score
                                           let rijTegelsTeruggelegdNieuw = legTegelTerug spelers rijTegels score
                                           let spelersAfgepaktNieuw = pakTegelAf spelers score
                                           let spelersNieuw = voegTegelToeAanSpelerVanStapel spelersAfgepaktNieuw score naam rijTegelsTeruggelegdNieuw
                                           putStrLn ("De beurt van speler " ++ naam ++ " is nu afgelopen.")
                                           printStatus rijTegelsNieuw spelersNieuw
                                           spelSpelen rijTegelsNieuw spelersNieuw ( (spelerNr+1) `mod` 3)  


 volleRijTegels :: [Tegel] -- deze functie maakt de rij tegels van 21 tot 36.
 volleRijTegels = zipWith Teg [21..36] (replicate 4 1 ++ replicate 4 2 ++ replicate 4 3 ++ replicate 4 4)

 deSpelers :: Int -> String -> [Speler]
 deSpelers aantalTegenspelers firstName = do [Spel computerTactiek [] firstName, Spel computerTactiek [] "Lap", Spel computerTactiek [] "Top"]

【问题讨论】:

您有许多缩进错误。除非有缩进的理由,否则代码必须缩进与前一行相同的量。多行 do 块的第二行必须缩进以匹配 do 关键字之后的文本。 修复缩进错误并为所有未定义变量添加占位符后,您发布的最终代码 sn-p 编译正常。 Here are my changes。 MCVE 会很有帮助。 【参考方案1】:

你做的一切都是正确的;缩进只是一个小问题。尝试将main 的最后一行缩进,如下所示:

 main :: IO ()  
 main = do
      -- ... stuff ...
      eindstand <- -maakNieuwSpel (digitToInt aantalTegenspelers) firstName- spelSpelen rijTegels spelers 0
      putStrLn ("Het spel is afgelopen. De eindstand is " ++ eindstand)

看看在我的版本中,eindstandputStrLn 前面有相同数量的空格吗?在您的版本中,putStrLn 前面有一个额外的空格,导致 GHC 认为它是spelSpelen 的参数。如果您删除多余的空间,它应该会开始工作。

【讨论】:

Hej 感谢您的回答,但事实并非如此。那只是将代码复制到***中的错误【参考方案2】:

这个问答可能更适合codereview.stackexchange.com。

除了基本的缩进问题之外,将main 拆分为多个部分是个好主意。

此外,您还需要尝试将其中的一些部分设为非IO。例如,应该可以在不触及任何IO 的情况下解决所有游戏逻辑。如果您的游戏逻辑中没有任何 getLineputStrLn 调用,则更容易测试,并且几乎没有错误来源,因为输入验证可以分离成自己的部分。

至于没有嵌入IO 的游戏逻辑会是什么样子,我不能说,因为游戏逻辑是用荷兰语写的,什么?如果您将此代码翻译成英文,或将代码的翻译副本重新提交到 codereview.stackexchange.com,有人可能会建议如何执行此操作。

【讨论】:

这有点跑题了,但我想知道在字母重命名为单字符变量名的情况下阅读代码是否更容易?荷兰语只是有点可理解这一事实使得解析变得更加困难。 @dfeuer:在我看来,这段代码只是通过将其复制粘贴到 Stack Overflow 中才被破坏的,但你是对的。

以上是关于如何在 Haskell 中将 main :: IO () 重写为由一个 main :: IO() 组成的两个函数的主要内容,如果未能解决你的问题,请参考以下文章

main函数不会在haskell中使用任何参数

Haskell IO 执行顺序

如何在 Haskell 中将数据类型转换为 BSON?

如何使用“ord”函数在 Haskell 中将 Char 转换为 Int?

AWS lambda 类似于 Haskell 函数的执行

Haskell IO 和关闭文件