在阅读"Haskell编程入门"这本书时,我遇到了一个相当令人困惑的练习。其中一个章节讲解如何使用闭包创建简单对象。例如,我们有一个元组来描述原始机器人:(名字,攻击力,血量)
。通过使用这个元组,我们可以像这样构造一个机器人:
robot (name,attack,hp) = \message -> message (name,attack,hp)
例如:
killerRobot = robot ("Kill3r", 25, 200)
接着,作者解释了如何利用这个结构体创建访问器函数:
hp (_,_,hp) = hp
attack (_,a,_) = a
getHP aRobot = aRobot hp
getAttack aRobot = aRobot attack
这样我们就可以检查某个机器人有多少命中点:
getHP killerRobot
目前为止一切都很好,我不打算重写整个章节,但是我还有一个问题没有明白。接下来我们有一个函数:
damage aRobot attackDamage = aRobot (\(n,a,h) ->
robot (n,a,h-attackDamage))
还有另一个函数
fight aRobot defender = damage defender attack
where attack = if getHP aRobot > 10
then getAttack aRobot
else 0
这个程序模拟了两个机器人之间的战斗。因此,我们可以编写类似于以下内容的代码:
gentleGiant = robot ("Mr. Friendly", 10, 300)
gentleGiantRound1 = fight killerRobot gentleGiant
killerRobotRound1 = fight gentleGiant killerRobot
gentleGiantRound2 = fight killerRobotRound1 gentleGiantRound1
killerRobotRound2 = fight gentleGiantRound1 killerRobotRound1
gentleGiantRound3 = fight killerRobotRound2 gentleGiantRound2
killerRobotRound3 = fight gentleGiantRound2 killerRobotRound2
它可以正常工作。但是当我试图将其放入一个函数中,这个函数封装了这些步骤并返回最后一步的结果(实际任务略有不同),我会遇到一堆与类型系统相关的错误。以下是一个简化版本,它会导致错误:
roundFights rb1 rb2 =
let rb2' = fight rb1 rb2
in fight rb2' rb1
第二个战斗会让编译器爆炸出错。所有这些函数都没有类型签名是有意的——因为它们只在书籍的介绍章节中出现,类型签名还没有被解释。
有人能建议一下出了什么问题吗?
以下是源代码:
robot (name, attack, hp) = \message -> message (name, attack, hp)
name (nm, _, _) = nm
attack (_, a, _) = a
hp (_, _, p) = p
getName r = r name
getAttack r = r attack
getHP r = r hp
setName r nm = r $ \(_, a, hp) -> robot (nm, a, hp)
setAttack r a = r $ \(nm, _, hp) -> robot (nm, a, hp)
setHP r hp = r $ \(nm, a, _) -> robot (nm, a, hp)
printRobot r = r $ \(nm, a, hp) -> nm ++ " attack:" ++ show a ++ " hp:" ++ show hp
damage r ad = r $ \(nm, a, hp) -> robot (nm, a, hp - ad)
fight atacker defender = damage defender power where
power = if getHP atacker > 10
then getAttack atacker
else 0
lives = map getHP
roundFights rb1 rb2 =
let rb2' = fight rb1 rb2
in fight rb2' rb1
rb1 = robot("Killer", 25, 200)
rb2 = robot("Slayer", 15, 200)
我得到的错误信息:
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:27:18:
Occurs check: cannot construct the infinite type:
t8 ~ ((t7, t8, t8) -> t0) -> t0
Expected type: ((t7, t8, t8) -> ((t7, t8, t8) -> t0) -> t0) -> t6
Actual type: ((t7, t8, t8) -> t8) -> t6
Relevant bindings include
rb2' :: ((t4, t5, t5) -> t5) -> t8
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:26:8)
rb2 :: ((t2, t3, t6) -> ((t2, t3, t6) -> t) -> t)
-> ((t4, t5, t5) -> t5) -> t8
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:25:17)
rb1 :: ((t7, t8, t8) -> t8) -> t6
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:25:13)
roundFights :: (((t7, t8, t8) -> t8) -> t6)
-> (((t2, t3, t6) -> ((t2, t3, t6) -> t) -> t)
-> ((t4, t5, t5) -> t5) -> t8)
-> t6
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:25:1)
In the second argument of `fight', namely `rb1'
In the expression: fight rb2' rb1
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:30:27:
No instance for (Num t1) arising from the literal `200'
The type variable `t1' is ambiguous
Relevant bindings include
rb1 :: (([Char], t1, t1) -> t) -> t
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:30:1)
Note: there are several potential instances:
instance Num Double -- Defined in `GHC.Float'
instance Num Float -- Defined in `GHC.Float'
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus three others
In the expression: 200
In the first argument of `robot', namely `("Killer", 25, 200)'
In the expression: robot ("Killer", 25, 200)
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:31:27:
No instance for (Num t1) arising from the literal `200'
The type variable `t1' is ambiguous
Relevant bindings include
rb2 :: (([Char], t1, t1) -> t) -> t
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:31:1)
Note: there are several potential instances:
instance Num Double -- Defined in `GHC.Float'
instance Num Float -- Defined in `GHC.Float'
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus three others
In the expression: 200
In the first argument of `robot', namely `("Slayer", 15, 200)'
In the expression: robot ("Slayer", 15, 200)
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:33:8:
No instance for (Ord t1) arising from a use of `fight'
The type variable `t1' is ambiguous
Relevant bindings include
rb2' :: (([Char], t1, t1) -> t) -> t
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:33:1)
Note: there are several potential instances:
instance Integral a => Ord (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Ord () -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
...plus 24 others
In the expression: fight rb1 rb2
In an equation for rb2': rb2' = fight rb1 rb2
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:34:8:
No instance for (Ord t1) arising from a use of `fight'
The type variable `t1' is ambiguous
Relevant bindings include
rb1' :: (([Char], t1, t1) -> t) -> t
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:34:1)
Note: there are several potential instances:
instance Integral a => Ord (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Ord () -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
...plus 24 others
In the expression: fight rb2' rb1
In an equation for rb1': rb1' = fight rb2' rb1
D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:35:9:
No instance for (Ord t1) arising from a use of `fight'
The type variable `t1' is ambiguous
Relevant bindings include
rb2'' :: (([Char], t1, t1) -> t) -> t
(bound at D:\Dropbox\Documents\Work\HS\GetProg\Unit 10\Robot.hs:35:1)
Note: there are several potential instances:
instance Integral a => Ord (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Ord () -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
...plus 24 others
In the expression: fight rb1' rb2'
In an equation for rb2'': rb2'' = fight rb1' rb2' Failed, modules loaded: none.
rb1'
和rb2''
,但在您的代码中却找不到它们)。您确定已经发布了实际的代码吗?也许您省略了某些部分? - Fyodor Soikinrb1'
或rb2''
的任何提及。 - Fyodor Soikindamage
函数有问题,但我还没想出为什么(没有类型信息让我很烦:P)。你可以试试这个定义:damage aRobot attackDamage = robot (getName aRobot, getAttack aRobot, getHP aRobot - attackDamage)
,应该能解决问题。 - nicodpdamage
函数,它是有效的。实际上,任务是(字面意思):编写一个threeRoundFight函数,它接受两个机器人并让它们进行三轮战斗,返回获胜者。为了避免有太多不同的变量用于机器人状态,使用一系列嵌套的lambda函数,这样你就可以只覆盖robotA和robotB。 - BarbedWire