学习Haskell,了解数据结构

时间:2018-07-17 05:48:51

标签: haskell

我实际上是在学习Haskell,并且正在尝试制作一个简单的FizzBu​​zz Kata。

这个想法是将一个数字列表作为条目,并生成一个数字列表|遵循以下规则的字符串:

  • fizzBu​​zz :: [StringInt]-> [StringInt]
  • 每3的倍数,列表中的项目应为“嘶嘶响”
  • 每5的倍数,列表中的项目应为“嗡嗡声”
  • 否则应该是数字

这是我产生的代码:

module FizzBuzz where 

data StringInt a = Int a | String a

handleRule:: StringInt a -> a
handleRule x
    | x % 3 == 0 = StringInt "Fizz"
    | x % 5 == 0 = StringInt "Buzz"
    | otherwise = x

run:: [StringInt a] -> [a]
run = map handleRule

尝试运行以下测试时:

module FizzBuzzSpec (spec) where

    import Test.Hspec
    import FizzBuzz

    spec :: Spec
    spec = describe "FizzBuzz#run" $ do
        it "should have displayed Fizz for number 3" $
            run [1..10]:2 `shouldBe` "Fizz"
        it "should have displayed Buzz for number 5" $
            run [1..10]:4 `shouldBe` "Buzz"

我有以下输出:

• Occurs check: cannot construct the infinite type: a ~ StringInt a
• In the expression: x
  In an equation for ‘handleRule’:
      handleRule x
        | (%) x 3 == 0 = StringInt "Fizz"
        | (%) x 5 == 0 = StringInt "Buzz"
        | otherwise = x
• Relevant bindings include
    x :: StringInt a (bound at src/FizzBuzz.hs:6:12)
    handleRule :: StringInt a -> a (bound at src/FizzBuzz.hs:6:1)

| 9 | |否则= x

重要说明:我真的是Haskell的新手,如果我在那做可怕的事情,对不起。

您知道我在做什么错吗?


编辑:

我尝试使用:

module FizzBuzz where 

type StringInt = Either Int String

handleRule:: Int -> StringInt
handleRule x
    | x `mod` 3 == 0 = Right "Fizz"
    | x `mod` 5 == 0 = Right "Buzz"
    | otherwise      = Left x

run:: [Int] -> [StringInt]
run = map handleRule

还有

module FizzBuzzSpec (spec) where

    import Test.Hspec
    import FizzBuzz

    spec::Spec
    spec = describe "FizzBuzz#run" $ do
        it "should have displayed Fizz for number 3" $
            run [1..10]:2 `shouldBe` Right "Fizz"
        it "should have displayed Buzz for number 5" $
            run [1..10]:4 `shouldBe` Right "Buzz"

但这总是抛出

    • Couldn't match expected type ‘[[StringInt]]’
                  with actual type ‘Either a0 [Char]’
    • In the second argument of ‘shouldBe’, namely ‘Right "Fizz"’
      In the second argument of ‘($)’, namely
        ‘run [1 .. 10] : 2 `shouldBe` Right "Fizz"’
      In a stmt of a 'do' block:
        it "should have displayed Fizz for number 3"
          $ run [1 .. 10] : 2 `shouldBe` Right "Fizz"
  |
9 |                         run [1..10]:2 `shouldBe` Right "Fizz"
  |                                                  ^^^^^^^^^^^^

/Users/pc/Soft/haskell/hello-stack/test/FizzBuzzSpec.hs:11:50: error:
    • Couldn't match expected type ‘[[StringInt]]’
                  with actual type ‘Either a1 [Char]’
    • In the second argument of ‘shouldBe’, namely ‘Right "Buzz"’
      In the second argument of ‘($)’, namely
        ‘run [1 .. 10] : 4 `shouldBe` Right "Buzz"’
      In a stmt of a 'do' block:
        it "should have displayed Buzz for number 5"
          $ run [1 .. 10] : 4 `shouldBe` Right "Buzz"
   |
11 |                         run [1..10]:4 `shouldBe` Right "Buzz"
   |                              

                ^^^^^^^^^^^^

无法弄清楚这里的意思...

感谢您的帮助人员

2 个答案:

答案 0 :(得分:2)

规则应如下所示:

data StringInt = SI_Str String
               | SI_Int Int

handleRule:: Int -> StringInt
handleRule x
    | x `mod` 3 == 0 = SI_Str "Fizz"
    | x `mod` 5 == 0 = SI_Str "Buzz"
    | otherwise      = SI_Int x

当您描述“数据”时,实际上是在描述数据类型。等式的左侧是类型的名称,在这种情况下,类型的名称是StringInt。在等式的右侧,您描述了如何创建数据类型的构造函数(或本例中的构造函数)。在这种情况下,我们有2个构造函数可以创建StringInt类型的“数据”-SI_Str构造函数和SI_Int构造函数。如何区分它们并访问它们的“数据内容”称为模式匹配。不过,为了不破坏学习的乐趣,我建议从那里开始进行学习。

答案 1 :(得分:1)

我假设是这样,

data StringInt a = Int a | String a

您将定义一个求和类型。但实际上,它需要提供数据构造函数:

data StringInt = Left Int | Right String

或者,已经有Either类型,它有助于定义求和类型,因此您只需创建类型别名即可。因此,您的程序将如下所示:

type StringInt = Either Int String

handleRule:: Int -> StringInt
handleRule x
    | mod x 3 == 0 = Right "Fizz"
    | mod x 5 == 0 = Right "Buzz"
    | otherwise = Left x

run:: [Int] -> [StringInt]
run = map handleRule

并且还应该修改测试,因为返回值不仅是字符串,而且是用Right包裹的字符串:

module FizzBuzzSpec (spec) where

import Test.Hspec
import FizzBuzz

spec::Spec
spec = describe "FizzBuzz#run" $ do
    it "should have displayed Fizz for number 3" $
        run [1..10] !! 2 `shouldBe` (Right "Fizz")
    it "should have displayed Buzz for number 5" $
        run [1..10] !! 4 `shouldBe` (Right "Buzz")

main :: IO()
main = hspec spec

运行测试:

runhaskell Tests.hs

顺便说一下,您现在可以跳过求和类型,只需返回数字的字符串表示形式(如果不能用35除),就可以了:

handleRule:: Int -> String
handleRule x
    | mod x 3 == 0 = "Fizz"
    | mod x 5 == 0 = "Buzz"
    | otherwise = show x

run:: [Int] -> [String]
run = map handleRule