【发布时间】:2020-11-11 05:36:48
【问题描述】:
我正在阅读《Get Programming with Haskell》一书:https://www.manning.com/books/get-programming-with-haskell
有一课以函数式的方式介绍OOP,以格斗机器人为例:
robot (name,attack,hp) = \message -> message (name,attack,hp)
killerRobot = robot ("Kill3r",25,200)
name (n,_,_) = n
attack (_,a,_) = a
hp (_,_,hp) = hp
getName aRobot = aRobot name
getAttack aRobot = aRobot attack
getHP aRobot = aRobot hp
setName aRobot newName = aRobot (\(n,a,h) -> robot (newName,a,h))
setAttack aRobot newAttack = aRobot (\(n,a,h) -> robot (n,newAttack,h))
setHP aRobot newHP = aRobot (\(n,a,h) -> robot (n,a,newHP))
nicerRobot = setName killerRobot "kitty"
gentlerRobot = setAttack killerRobot 5
softerRobot = setHP killerRobot 50
printRobot aRobot = aRobot (\(n,a,h) -> n ++
" attack:" ++ (show a) ++
" hp:"++ (show h))
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
fastRobot = robot ("speedy", 15, 40)
slowRobot = robot ("slowpoke",20,30)
fastRobotRound3 = fight slowRobotRound3 fastRobotRound2
fastRobotRound2 = fight slowRobotRound2 fastRobotRound1
fastRobotRound1 = fight slowRobotRound1 fastRobot
slowRobotRound2 = fight fastRobotRound1 slowRobotRound1
slowRobotRound3 = fight fastRobotRound2 slowRobotRound2
slowRobotRound1 = fight fastRobot slowRobot
我看到代码底部的示例战斗如何必须创建新变量来存储每次战斗后创建的对象,因为每个对象实际上只是一个存储其状态的闭包,并且闭包需要绑定到某些东西。
我很好奇是否可以使用damage 函数连续多次攻击机器人。之前的课介绍了高阶函数,包括foldl,所以我尝试多次使用它来破坏gentleGiant机器人:
pummeledGiant = foldl damage gentleGiant [100,50,100,500]
但我收到此错误:
• Occurs check: cannot construct the infinite type:
t1 ~ (([Char], Integer, Integer) -> t1) -> t1
Expected type: ((([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> Integer
-> (([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer) -> t1)
-> t1
Actual type: ((([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer) -> t1)
-> t1)
-> Integer
-> (([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer) -> t1)
-> t1
• In the first argument of ‘foldl’, namely ‘damage’
In the expression: foldl damage gentleGiant [100, 50, 100, 500]
In an equation for ‘pummeledGiant’:
pummeledGiant = foldl damage gentleGiant [100, 50, 100, ....]
• Relevant bindings include
pummeledGiant :: (([Char], Integer, Integer)
-> (([Char], Integer, Integer) -> t1) -> t1)
-> (([Char], Integer, Integer) -> t1) -> t1
(bound at <interactive>:2:1)
我的理解是 foldl 需要 3 个参数:
- 二元函数
- 一个初始值
- 值列表
并以向左的方式逐步将二进制函数应用于值对,从初始值和值列表中的第一个值开始,“链接”结果,例如:
foldl (+) 0 [1..3] = ((0 + 1) + 2) + 3
我不明白我遇到的错误,也看不出我使用foldl 的方式有任何逻辑问题。我做错了什么?
【问题讨论】:
-
说实话,这段代码我什么都看不懂。也许您应该尝试在定义中添加类型。