我如何遍历n个元素列表并在每个元素上应用一个函数

时间:2018-10-24 18:14:10

标签: haskell

正确的玩家获胜,如果对方执行-1,则该函数返回1

evalTurn :: Symbol -> Symbol -> Outcome
evalTurn x y | x == y = 0
evalTurn 0 1 = 1
evalTurn 0 2 = -1
evalTurn 1 2 = 1
evalTurn 0 1 = -1
evalTurn 2 0 = 1
evalTurn 2 1 = -1
evalTurn 1 0 = -1

我需要使用上面的真值表评估以下内容。

evalBunch (x,y,z) = evalTurn x + evalTurn y + evalTurn z

evalMatch :: ([Symbol], [Symbol]) -> Outcome
evalMatch (x,y) = evalBunch (zip x y)

测试用例向每个回合的2位玩家展示结果,总结

evalMatch ([1,1,1], [0,0,0]) == -3

2 个答案:

答案 0 :(得分:5)

首先,考虑为您的值使用更多以域为中心的数据类型,而不是仅使用具有特殊含义的整数。例如看起来您在这里玩的是剪刀石头布,所以您会喜欢上这样的东西

data Symbol = Rock | Paper | Scissors deriving Eq
data Outcome = Win | Lose | Tie

evalTurn :: Symbol -> Symbol -> Outcome
evalTurn x y | x == y = Tie
evalTurn Rock Paper = Lose
-- ...

照原样,您不小心在evalTurn中重复了一个案例:既有evalTurn 0 1 = 1,又有evalTurn 0 1 = -1。如果您使用更丰富的域模型,这将更容易发现。或者,如果您想坚持使用整数,则实际上是使用模块化算法的捷径:

evalTurn x y = case (y - x) `mod` 3 of
  0 -> 0
  1 -> 1
  2 -> -1

关于您的实际问题,在您要尝试解决的问题之前,存在许多问题。 evalTurnevalBunchevalMatch在符号类型应该是什么以及evalTurn实际需要多少个参数上都存在分歧。像evalTurn那样用一个参数调用evalBunch是否有意义?当然不会。

我的第一个建议是完全忘记此evalBunch函数,而直接用evalMatch来写evalTurn。对您来说,最好递归地手工编写它,而不要使用花哨的zip,咖喱和函数组合。


如果您喜欢我建议的功能更丰富的域模型,但又不想写出全部9种情况(如果您简化x == y用例,则是7种),您可以做的另一件事是定义“确实X击败Y”作为布尔值原语,然后以此为基础来决定谁赢得比赛的更复杂问题:

beats :: Symbol -> Symbol -> Bool
Rock `beats` Scissors = True
Scissors `beats` Paper = True
Paper `beats` Rock = True
_ `beats` _ = False

evalTurn :: Symbol -> Symbol -> Outcome
evalTurn x y | x `beats` y = Win
             | y `beats` x = Lose
             | otherwise = Tie

它并没有真正缩短,甚至更长。但是它避免了重复,并且作为一个好的好处,该程序现在在evalTurn上下文以外的地方“了解”了一些有关Symbols的更多知识,这可能在程序的另一部分中被证明是有用的。例如,您可以使用它来画出什么胜过什么的教程图。

答案 1 :(得分:0)

稍微介绍一下最初出现问题的方式,我建议进行以下简化:

type Symbol = Int
type Outcome = Int

evalTurn :: (Symbol, Symbol) -> Outcome
evalTurn (x, y)
  | (x == y)           =  0
  | (x == 0 && y == 1) =  1
  | (x == 0 && y == 2) = -1
  | (x == 1 && y == 2) =  1
  | (x == 2 && y == 0) =  1
  | (x == 2 && y == 1) = -1
  | (x == 1 && y == 0) = -1

evalBunch :: (Foldable t, Functor t) => t (Symbol, Symbol) -> Outcome
evalBunch v = sum $ fmap evalTurn v

--Pointfree style is more elegant
--evalBunch = sum . fmap evalTurn

那么,这个功能不能解决您的用例吗?

Prelude> evalBunch [(1, 0), (1,0), (1,0)] 
Prelude> -3