【问题标题】:Can someone diagnose the issue with my Haskell code?有人可以用我的 Haskell 代码诊断问题吗?
【发布时间】:2020-08-10 12:38:36
【问题描述】:

我感觉我从根本上误解了 Haskell 的编写方式。我的代码旨在作为游戏 Othello 的原始 AI 的评估函数。本质上,我希望我的代码在一个片段列表中循环,每个片段由一个由位置(Int,Int)和一个 Col(颜色,黑色或白色)组成的元组表示,并给出一个预期的移动来确定移动的好坏是。

我判断移动价值的方式基于几个因素:

  • 棋盘上是否有一块与预定位置成直线的同色棋子?
  • 如果前面的要求为真,那两块之间有多少相反颜色的块?

由于 Haskell 没有循环结构,我似乎需要递归地实现它,因此我的代码如下:

eval :: Position -> [(Position, Col)] -> Col -> GameState -> Int -> Int
eval move pieces moveColour gameState score = do
                                                let moveX = fst move
                                                let moveY = snd move
                                                let piece = head(pieces)
                                                let pieceColour = snd piece
                                                let pieceX = fst fst piece
                                                let pieceY = snd fst piece
                                                if (moveColour == pieceColour) then
                                                  if (moveX == pieceX) then
                                                    if (moveY > pieceY) then
                                                      let newScore = score + (countOtherColour 0 moveY pieceY (pieces gameState) moveColour 0)
                                                      --recurse
                                                      if (tail(pieces) == []) then
                                                        return newScore
                                                      else
                                                        return eval move tail(pieces) moveColour gameState newScore
                                                    else
                                                      let newScore = score + (countOtherColour 0 pieceY moveY (pieces gameState) moveColour 0)
                                                      --recurse
                                                      if (tail(pieces) == []) then
                                                        return newScore
                                                      else
                                                        return eval move tail(pieces) moveColour gameState newScore
                                                  else
                                                    if (moveY == pieceY) then
                                                      if (moveX > pieceX) then
                                                        let newScore = score + (countOtherColour 1 moveX pieceX (pieces gameState) moveColour 0)
                                                        --recurse
                                                        if (tail(pieces) == []) then
                                                          return newScore
                                                        else
                                                          return eval move tail(pieces) moveColour gameState newScore
                                                      else
                                                        let newScore = score + (countOtherColour 1 pieceX moveX (pieces gameState) moveColour 0)
                                                        --recurse
                                                        if (tail(pieces) == []) then
                                                          return newScore
                                                        else
                                                          return eval move tail(pieces) moveColour gameState newScore
                                                    else
                                                      --recurse
                                                      if (tail(pieces) == []) then
                                                        return score
                                                      else
                                                        return eval move tail(pieces) moveColour gameState score
                                                else
                                                  --recurse
                                                  if (tail(pieces) == []) then
                                                    return score
                                                  else
                                                    return eval move tail(pieces) moveColour gameState score

countOtherColour :: Int -> Int -> Int -> [(Position, Col)] -> Col -> Int -> Int
countOtherColour xyFlag upper lower pieces turnColour score = do
                                                                --if xyFlag == 0 it's y aligned if 1 it's x aligned
                                                                let piece = head(pieces)
                                                                let pieceColour = other (snd piece)
                                                                let x = fst fst piece
                                                                let y = snd fst piece
                                                                if (pieceColour == turnColour) then
                                                                  if (xyFlag == 0) then
                                                                    if (upper > x && x > lower) then
                                                                      let newScore = score+1
                                                                      --recurse
                                                                      if (tail(pieces) == []) then
                                                                        return newScore
                                                                      else
                                                                        return countOtherColour xyFlag upper lower tail(pieces) turnColour newScore
                                                                    else
                                                                      --recurse
                                                                      if (tail(pieces) == []) then
                                                                        return score
                                                                      else
                                                                        return countOtherColour xyFlag upper lower tail(pieces) turnColour score
                                                                  else
                                                                    if (upper > y && y > lower) then
                                                                      let newScore = score+1
                                                                      --recurse
                                                                      if (tail(pieces) == []) then
                                                                        return newScore
                                                                      else
                                                                        return countOtherColour xyFlag upper lower tail(pieces) turnColour newScore
                                                                    else
                                                                      --recurse
                                                                      if (tail(pieces) == []) then
                                                                        return score
                                                                      else
                                                                        return countOtherColour xyFlag upper lower tail(pieces) turnColour score
                                                                else
                                                                  --recurse
                                                                  if (tail(pieces) == []) then
                                                                    return score
                                                                  else
                                                                    return countOtherColour xyFlag upper lower tail(pieces) turnColour score

但是,此代码无法编译。我在第一行收到“if”解析错误:

if (tail(pieces) == []) then

这让我相信,关于我构建这段代码的方式的基本原理是错误的。我想澄清一下,我不是寻找某人来为我解决实现,只是为了有人向我解释我的实现如何存在缺陷,以及我如何在其中实现递归的一般指南构建我的代码的正确方法。

如果您已经阅读了这么多,谢谢,我期待阅读您的回复。

【问题讨论】:

  • 一条评论:您的代码似乎比必要的要冗长和复杂。这里有一些关于你可以做些什么来清理它的建议:(1)你有比必要更多的缩进。您不需要将do 块缩进那么远——只需两个或四个空格就足够了。 (2)你有很多重复;您可以尝试通过将所有重复的位移动到它们自己的函数中来减少这种重复。
  • @bradrn 感谢您的建议,我将对缩进进行排序,但我不确定您是否注意到,但取决于递归部分(具有重复的部分)的上下文递归必须以不同的方式调用。我不确定如何将它转移到它自己的函数中,同时保持根据上下文稍微改变调用方式的能力。
  • 我确实注意到了。将其分离到另一个函数中并不重要:只需将变化的位作为参数提供给该函数,并每次使用不同的参数调用该函数。

标签: list if-statement haskell recursion iteration


【解决方案1】:

letdo 之外时,它需要in,例如:

-- correct
x =
    let a = 5
    in a + 37

-- incorrect
x =
    let a = 5
    a + 37

所以当你的if 紧跟在let 之后时,它前面应该有一个in

let newScore = score+1 
-- recurse
in if (tail(pieces) == []) then
       ...

除此之外,您的代码中还有很多其他内容可以更简洁。我不会一一列举,只举几个例子。

一个例子是连续的多个let绑定不需要为每个单独的let

x =
    let a = 5
        b = 37
    in a + b

同样,if 的条件不需要括号,函数调用也不需要:

if tail pieces == [] then

当我们这样做时:通过null 函数测试空列表,而不是比较== []

if null (tail pieces) then

【讨论】:

  • 我很惊讶你似乎没有在这里提到最基本的误解:使用 do 块作为应该导致 Int 的函数。
  • 您的第一个“正确”示例不正确,因为它使用了两个 lets 但只有一个 in。我不确定您是要删除第二个 let(正常的 Haskell 样式),还是添加第二个 in(以延迟显示多个绑定)。
  • @RobinZigmond 我很尴尬地说,我根本没有注意到。那里有很多:-)
  • do 表示法用于单子。它是运算符>>= 的语法糖(查看the wiki)。 Int 类型不是 monad,也没有定义 >>= 运算符,因此 do 表示法不起作用。但它有点复杂,因为a -> b 类型(对于任何ab)实际上是一个monad,并且确实定义了运算符>>=,所以编译器可能会考虑在 monad Int -> Int 中实现整个函数,并因此给你看起来很奇怪的错误。
  • 一般规则是:永远不要假设,始终确保在使用它们之前真正知道事物的含义。
猜你喜欢
  • 2015-07-30
  • 1970-01-01
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多