Haskell - 对列表的递归调用

时间:2014-08-11 18:46:42

标签: haskell recursion

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

    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)

如何以递归方式调用函数makemovesonemove返回一个3元组,而makemoves则期待一个4元组,这是返回的3元组加上下一个移动。谢谢!

3 个答案:

答案 0 :(得分:3)

  一个人回归过三个元组,而另一个人则期待一个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)

我不确定那些条款的用途是什么;没有一个被使用。

编辑:实际上,忽略以下内容,因为您需要基于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]])

答案 1 :(得分:1)

这样的行
          copyTime = time

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

为了解决您的问题,让我们考虑一个更简单的例子:不是移动,而是您尝试添加的简单数字。用元组编写(尽管如Mark Markfield所说,curried函数更好!)

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

答案 2 :(得分:0)

所以我想出来了,这是解决方案

     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

将该临时拥护者传递到移动列表中的下一个动作