无法让这个琐碎的哈克尔计划奏效

时间:2018-06-15 18:15:29

标签: haskell

我还在学习haskell,我发现了一个使用以下hangman简单程序介绍IO概念的教程

import System.IO
import System.Random

main = do
  handle <- openFile "enable1.txt" ReadMode 
  contents <- hGetContents handle           
  gen <- getStdGen                          
  let words = map init (lines contents)     
      (n, _) = randomR(0, (length words) - 1) gen :: (Int, StdGen) 
      word = words !! n                                         
  play word (map (\x -> '_') word) 6        
  build the print the string
  hClose handle                           

play word known guesses
  | word == known = do
      putStrLn known
      putStrLn "You win!!"
  | guesses == 0 = do
      putStrLn known
      putStrLn ("You lose. the word was " ++ word ++ ".")
  | otherwise    = do
      putStrLn known
      putStrLn ("You have " ++ guesses ++ "guesses left.")
      line <- getLine
      let (newKnown, newGuesses) = handle (head line) word known guesses
      play word newKnown newGuesses

    --putStrLn (handle (head line) word)

handle letter word known guesses
  | letter `elem` word = (zipWith (\w k -> if w == letter then w else k) word known, guesses)
  | otherwise          = (known, guesses - 1)

enable1.txt是一个包含大量单词的本地文件。 我使用runhaskill运行该文件 我收到以下错误:

:~/Documents/atom/haskell$ runhaskell hangman.hs 

hangman.hs:22:36: error:
    • No instance for (Num [Char]) arising from the literal ‘6’
    • In the third argument of ‘play’, namely ‘6’
      In a stmt of a 'do' block: play word (map (\ x -> '_') word) 6
      In the expression:
        do { handle <- openFile "enable1.txt" ReadMode;
             contents <- hGetContents handle;
             gen <- getStdGen;
             let words = map init (lines contents)
                 (n, _) = ...
                 ....;
             .... }

hangman.hs:30:16: error:
    • No instance for (Num [Char]) arising from the literal ‘0’
    • In the second argument of ‘(==)’, namely ‘0’
      In the expression: guesses == 0
      In a stmt of a pattern guard for
                     an equation for ‘play’:
        guesses == 0

hangman.hs:37:36: error:
    • No instance for (Num [Char]) arising from a use of ‘handle’
    • In the expression: handle (head line) word known guesses
      In a pattern binding:
        (newKnown, newGuesses) = handle (head line) word known guesses
      In the expression:
        do { putStrLn known;
             putStrLn ("You have " ++ guesses ++ "guesses left.");
             line <- getLine;
             let (newKnown, newGuesses) = handle (head line) word known guesses;
             .... }

任何人都可以帮助我了解问题/如何解决它。 runhaskell --version是runghc 8.0.2

3 个答案:

答案 0 :(得分:3)

其他人已经在您的代码中指出了一些问题。在这里,我只想为您提供一般性建议。

大多数Haskellers,包括&#34; expert&#34;,总是从类型注释开始编写任何新的顶级函数(或一般的绑定)。也就是说,写foo :: Type1 -> Type2 -> ... -> ReturnType。实际上,强烈建议这有几个原因。

首先,它有助于程序员专注于他们必须处理或生成的数据类型。对于简单的程序来说,这在程序员的脑海中可能是显而易见的,但在更严肃的高级代码中它变得不那么重要。

其次,它阻止类型推理引擎推断出非预期的类型。例如,请考虑此代码。

foo x = "hello" ++ x   -- line A

这可以毫无问题地被接受,并且x被GHC推断为String类型。

然而,在程序员的心中x应该是一个整数,所以,稍后,程序员会写

let s = foo 42 in ...  -- line B

并且GHC抱怨42不是String。或者更糟糕的是,Num String无法满足,这意味着字符串不是数字类型。现在程序员很困惑,因为GHC指向B行作为问题,但该代码对程序员来说很好。 &#34;我传递一个整数,foo需要一个整数,这个奇怪的字符串错误来自哪里?!?&#34;

这不是编译器的错 - 它无法知道A行中的代码是错误的。但是,如果程序员告诉编译器,在A行附近,x本来是一个整数,那么它确实是编译器的错误!编译器现在应该抱怨A行中的错误!事实上,确实如此:这是一个GHCi快速测试。

>  foo :: Int -> String ; foo x = "hello" ++ x
error:
    • Couldn't match expected type ‘[Char]’ with actual type ‘Int’
    • In the second argument of ‘(++)’, namely ‘x’
      In the expression: "hello" ++ x
      In an equation for ‘foo’: foo x = "hello" ++ x

阿公顷! ++需要一个字符串,但x是一个整数。所以我们必须转换它

> foo :: Int -> String ; foo x = "hello" ++ show x

现在,没有错误发生。

一般来说,当编码并犯一些错误时,可能会导致GHC推断出非预期的类型,导致后来出现令人费解的错误,指向看似完美的代码。在这种情况下,常见的技术是添加越来越多的类型注释,通知编译器程序员的意图,以便GHC可以产生更有意义的错误。最终,GHC和程序员都认为出了问题,而且可以修复bug。

答案 1 :(得分:2)

您收到错误,因为程序中的类型不一致。

  • X表示“[Char]不是任何数字”。
  • String是字符串的类型(play是其别名)。
  • 因此,您的错误意味着某些内容被用作字符串和数字。

查看您的代码,我可以在"You have " ++ guesses ++ "guesses left."中看到

  • guesses表示guesses == 0必须是字符串,才能与其他字符串连接。
  • guesses表示show必须是数字。

如果您从教程中获得了此代码,那么这是一个编写得很糟糕的教程,您应该找到一个更好的教程。如果您是根据教程自己编写的,那么您一定错过了一步。

为了将数字转换为字符串进行打印,您可以使用"You have " ++ show guesses ++ "guesses left." 函数:

{{1}}

答案 2 :(得分:1)

您不能(++) String和数字类型。您应首先使用函数show将数字转换为String,然后才能将其与另一个字符串连接。

在您的代码中,参数wordknown属于某种类型Num a => a,但(++)只接受两个String(即[Char] )作为参数(准确地说,它接受两个相同类型元素的列表,并且因为您已经应用了String,所以另一个参数也应该是String)。因此,您应将word替换为show word,将known替换为<div style="height: 300px;" leaflet [leafletOptions]="options"> </div>