Haskell 无法让这个琐碎的haskel程序工作

Haskell 无法让这个琐碎的haskel程序工作,haskell,Haskell,我还在学习haskell,我发现了一个教程,它使用以下hangman简单程序介绍了IO概念 import System.IO import System.Random main = do handle <- openFile "enable1.txt" ReadMode contents <- hGetContents handle gen <- getStdGen let words

我还在学习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-版本是Runghc8.0.2

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

在您的代码中,参数word和known的类型为Num a=>a,但是++只接受两个字符串,即[Char]作为参数。准确地说,它接受两个相同类型元素的列表,并且由于您已经对其应用了字符串,所以另一个参数也应该是字符串。所以你应该用show-word替换word,对known也一样

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


在您的代码中,参数word和known的类型为Num a=>a,但是++只接受两个字符串,即[Char]作为参数。准确地说,它接受两个相同类型元素的列表,并且由于您已经对其应用了字符串,所以另一个参数也应该是字符串。所以你应该用show-word替换word,对known也一样

由于程序中的类型不一致,因此出现错误

没有Num X的实例意味着X不是任何类型的数字。 [Char]是字符串的类型字符串是它的别名。 因此,您的错误意味着某些内容被用作字符串和数字。 通过查看您的代码,我可以看到您的代码正在发挥作用

你还有++猜测++猜测。这意味着猜测必须是一个字符串,以便与其他字符串连接。 猜测==0,这意味着猜测必须是一个数字。 如果您是从教程中获得这段代码的,那么这是一个写得很糟糕的教程,您应该找到一个更好的教程。如果你是根据教程自己写的,那么你肯定错过了一步

要将数字转换为字符串进行打印,可以使用显示功能:

"You have " ++ show guesses ++ "guesses left."

由于程序中的类型不一致,因此出现错误

没有Num X的实例意味着X不是任何类型的数字。 [Char]是字符串的类型字符串是它的别名。 因此,您的错误意味着某些内容被用作字符串和数字。 通过查看您的代码,我可以看到您的代码正在发挥作用

你还有++猜测++猜测。这意味着猜测必须是一个字符串,以便与其他字符串连接。 猜测==0,这意味着猜测必须是一个数字。 如果您是从教程中获得这段代码的,那么这是一个写得很糟糕的教程,您应该找到一个更好的教程。如果你是根据教程自己写的,那么你肯定错过了一步

要将数字转换为字符串进行打印,可以使用显示功能:

"You have " ++ show guesses ++ "guesses left."

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

大多数Haskeller(包括专家)总是从类型注释开始编写任何新的顶级函数或绑定。也就是说,通过编写foo::Type1->Type2->…->返回类型。事实上,出于几个原因,强烈建议这样做

首先,它帮助程序员集中精力处理或生成什么样的数据。对于简单的程序,这在程序员的头脑中可能是显而易见的,但在更严肃、更高级的代码中,它就变得不那么琐碎了

其次,它防止类型推断引擎推断出意外的类型。例如,考虑这个代码。

foo x = "hello" ++ x   -- line A
这是可以接受的,没有问题,并且GHC推断x为String类型

然而,在程序员的心目中,x应该是一个整数,因此,稍后,程序员会编写

let s = foo 42 in ...  -- line B
GHC抱怨42不是字符串。甚至更糟糕的是,无法满足Num字符串,这意味着字符串不是数字类型。现在程序员感到困惑,因为GHC指出B行是问题所在,但程序员认为代码看起来很好。我在传递一个整数,foo需要一个整数,这个奇怪的字符串错误是从哪里来的

这不是编译器的错——它无法知道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可能会推断出非预期的类型,导致以后出现令人费解的错误,p
指向看似完美的代码。在这种情况下,一种常见的技术是添加越来越多的类型注释,将程序员的意图告知编译器,以便GHC能够产生更多有意义的错误。最终,GHC和程序员一致认为出现了问题,错误可以修复。

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

大多数Haskeller(包括专家)总是从类型注释开始编写任何新的顶级函数或绑定。也就是说,通过编写foo::Type1->Type2->…->返回类型。事实上,出于几个原因,强烈建议这样做

首先,它帮助程序员集中精力处理或生成什么样的数据。对于简单的程序,这在程序员的头脑中可能是显而易见的,但在更严肃、更高级的代码中,它就变得不那么琐碎了

其次,它防止类型推断引擎推断出意外的类型。例如,考虑这个代码。

foo x = "hello" ++ x   -- line A
这是可以接受的,没有问题,并且GHC推断x为String类型

然而,在程序员的心目中,x应该是一个整数,因此,稍后,程序员会编写

let s = foo 42 in ...  -- line B
GHC抱怨42不是字符串。甚至更糟糕的是,无法满足Num字符串,这意味着字符串不是数字类型。现在程序员感到困惑,因为GHC指出B行是问题所在,但程序员认为代码看起来很好。我在传递一个整数,foo需要一个整数,这个奇怪的字符串错误是从哪里来的

这不是编译器的错——它无法知道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和程序员一致认为出现了问题,错误可以修复