【问题标题】:Haskell--Recursive calls on ListsHaskell——对列表的递归调用
【发布时间】:2014-08-12 03:40:10
【问题描述】:

我有一个函数onemove,我正在递归调用列表moves。函数onemove返回一个三元组(Int,[Char],[[Char]]),即timecapturesboard。对于moves 列表中的每个移动,我想在输入元组上调用函数onemove。到目前为止,我在下面提供了我的代码:

    makemoves :: (Int,[Char],[[Char]],[(Int,Int)]) -> (Int,[Char],[[Char]])

    makemoves (time, captures, board, [] ) = (time, captures, board)
    makemoves (time, captures, board, (moveFrom, moveTo):moves ) 
        | checkerAlive board = makemoves (onemove (time, captures, board, move:moves)) 
        | otherwise          = resetGame (time, captures, board)
        where    copyTime = time
                 copyCaptures = captures
                 copyBoard = board
                 move = (moveFrom,moveTo)
                 tempTuple = onemoving (time, captures, board, move)

如何递归调用函数makemovesonemovereturns 一个 3 元组,而 makemoves 期望一个 4 元组,即返回的 3 元组加上下一步。谢谢!

【问题讨论】:

  • 如果这个类型检查,为什么onemove的类型签名和makemoves一样?
  • 我没有从你的代码中得到一些东西。例如为什么你有一个copyBoard = board?此外,您可以使用@ 模式:move@(moveFrom, moveTo):moves,而不是在 where 子句中定义 move。关于您的问题,在某些情况下,编写返回冗余信息的辅助函数并通过调用它并删除不需要的信息来编写实际函数会更容易。懒惰也会避免实际计算不需要的信息。

标签: haskell recursion


【解决方案1】:

onemove 返回一个 3 元组,而 makemoves 期望一个 4 元组, 这是返回的 3 元组加上下一步。谢谢!

不,它需要一个列表剩余的移动,你已经在moves 中。

还记得前几天我告诉过你让你的函数接受多个参数而不是一个元组中的所有参数吗?这正是原因。你现在应该这样做:

makemoves :: [(Int,Int)] -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])

makemoves [] (time, captures, board) = (time, captures, board)
makemoves (move:moves) (time, captures, board)
    | checkerAlive board = makemoves moves (onemove (time, captures, board, move:moves)) 
    | otherwise          = resetGame (time, captures, board)

我不确定所有 where 子句的用途;没有一个被使用。

编辑:实际上,请忽略以下内容,因为您需要基于 checkerAlive 函数的提前退出逻辑。它仍然可以写成折叠,但在这一点上可能比它的价值更多。

此外,您在此处编写的是函数式编程中非常常见的递归模式,称为“折叠”。整个事情可以重写为:

makemoves :: [(Int,Int)] -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])
makemoves moves env = foldr onemoveOrReset env moves
onemoveOrReset :: (Int,Int) -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])

【讨论】:

  • 不幸的是,我的教授对声明的输入/输出元组设置了要求,所以我必须以某种方式解决这个想法
  • @Dev 你可能想看看Data.Tuple.Curry
  • @Dev:这个要求相当令人讨厌。 有时 使用元组而不是 curried 函数是合理的,但 Haskell 程序员通常更喜欢前者。如果您的教授对正确的 Haskell 编程有所了解,他们也会很乐意接受柯里化解决方案。
  • @Dev 我同意leftaroundabout,作为一个Haskeller,我更喜欢那些拆分的元组、给定的名称,甚至可能是完整的记录类型,因为它使代码更具可读性、可理解性和简洁性。在 Haskell 中,使用巨型元组通常是一种代码味道。这条规则也有例外,但一般来说,一个 2 元组与您想要的一样高。我会说这样做的主要原因是因为标准的 Haskell 库没有很多工具可以处理更大的元组,而在那之后,为字段提供明确的名称确实很有帮助。
【解决方案2】:

类似的行

          copyTime = time

在 Haskell 中完全流利:复制 任何东西都没有意义。只有值,没有可能在某个时候改变的“对象”,并且可以在任何地方使用值。所以你的整个where 块是没用的。

为了解决您的问题,让我们考虑一个更简单的示例:不是移动,而是您尝试添加的简单数字。用元组编写(不过,正如 Mark Whitfield 所说,柯里化函数更好!)

addOne :: (Int,Int) -> Int
addOne (num,inc) = num + inc

makeAdds :: (Int, [Int]) -> Int
makeAdds (num, []) = num

好的,现在是递归的情况。目前,在您的代码中,您基本上拥有

makeAdds (num, inc:incs) = makeAdds (addOne (num, inc:incs))

这是错误的,原因有两个:

  • addOne 不能使用列表,但您正在处理它 inc:incs – 您刚刚从输入中获得的列表。显然,您只想使用 head 元素,即单独使用 inc
  • makeAdds 需要两个参数,第二个是一个列表。好吧,显然这是剩下的列表!

    makeAdds (num, inc:incs) = makeAdds (addOne (num, inc), incs)

很简单。

也就是说,这一切的“正确”版本当然是

addOne :: Int -> Int -> Int
addOne = (+)                   -- Yeah, you can do that.

makeAdds :: [Int] -> Int -> Int  -- Observe I've switched the order:
                                 -- "state" arguments best come last, useful with partial application

makeAdds [] = id
makeAdds (inc:incs) = makeAdds incs . addOne inc

【讨论】:

    【解决方案3】:

    所以我想通了,这就是解决方案

         makemoves (time, captures, board, (moveFrom, moveTo):moves ) 
            | checkerAlive board = makemoves (newTime,newCaptures,newBoard,moves) 
            | otherwise          = resetGame (time, captures, board)
           where move = (moveFrom,moveTo)
                 (newTime,newCaptures,newBoard) = onemoving (time, captures, board, move)
    

    您必须首先为第一个移动创建一个临时持有者,因此 (newTime,newCaptures,newBoard) 然后通过调用 makemoves 将该临时持有者传递给移动列表中的下一个移动

    【讨论】:

    • 请注意,您可以省略 (moveFrom, moveTo) 元组并立即匹配 move:moves,因为您不会单独使用 moveFrommoveTo。除此之外,这与那些丑陋的非咖喱函数一样好。
    猜你喜欢
    • 2019-07-21
    • 2022-10-13
    • 2013-01-17
    • 2011-07-16
    • 1970-01-01
    • 2019-12-02
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    相关资源
    最近更新 更多