Haskell——列表上的递归调用

Haskell——列表上的递归调用,haskell,recursion,Haskell,Recursion,我有一个函数onemove,我正在递归调用一个列表moves。函数onemove返回一个三元组(Int、[Char]、[Char]]),即time、捕获和板。我想为moves列表中的每个移动调用输入元组上的函数onemove。以下是我迄今为止提供的代码: makemoves :: (Int,[Char],[[Char]],[(Int,Int)]) -> (Int,[Char],[[Char]]) makemoves (time, captures, board, [] )

我有一个函数
onemove
,我正在递归调用一个列表
moves
。函数
onemove
返回一个三元组
(Int、[Char]、[Char]])
,即
time
捕获
。我想为
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)
如何递归调用函数
makemoves
onemove
返回一个3元组,而makemoves需要一个4元组,即返回的3元组加上下一个move。谢谢

OneMoves返回一个3元组,而makemoves需要一个4元组, 这是返回的3元组加上下一步。谢谢

不,它需要一个剩余移动的列表,您已经在
移动中找到了该列表

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

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
函数的提前退出逻辑。它仍然可以写成折页,但在这一点上可能比它的价值更大

另外,您在这里写的是函数式编程中非常常见的递归模式,称为“fold”。这整件事可以改写为:

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]])
OneMoves返回一个3元组,而makemoves需要一个4元组, 这是返回的3元组加上下一步。谢谢

不,它需要一个剩余移动的列表,您已经在
移动中找到了该列表

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

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
函数的提前退出逻辑。它仍然可以写成折页,但在这一点上可能比它的价值更大

另外,您在这里写的是函数式编程中非常常见的递归模式,称为“fold”。这整件事可以改写为:

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]])
线条像

          copyTime = time
在Haskell中完全是多余的:复制任何东西都没有意义。只有值,没有可能在某一点上改变的“对象”,并且值可以在任何地方使用。因此,您的整个
where
块是无用的

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

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

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
线条像

          copyTime = time
在Haskell中完全是多余的:复制任何东西都没有意义。只有值,没有可能在某一点上改变的“对象”,并且值可以在任何地方使用。因此,您的整个
where
块是无用的

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

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

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

所以我想出来了,这是解决办法

     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将临时保持器传递到移动列表中的下一步,因此我找到了解决方案

     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

将临时保持架传递到移动列表中的下一步,如果此类型检查,为什么
onemove
的类型签名与
makemoves
的类型签名相同?我从您的代码中没有得到一些东西。例如,为什么您有一个
copyBoard=board
?也可以使用
@
模式,而不是在
where
子句中定义
move
move@(moveFrom,moveTo):moves
。关于您的问题,在某些情况下,编写一个返回冗余信息的helper函数,并通过调用它并删除不需要的信息来编写实际函数更容易。懒散也会避免实际计算不需要的信息。如果这种类型检查,为什么
onemove
的类型签名与
makemoves
相同?我没有从您的代码中得到一些东西。例如,你为什么有