如何将递归函数转换为尾递归版本?

时间:2019-04-13 16:44:54

标签: scala recursion

我有一个似乎无法使尾递归的函数。

我尝试使用额外的累加器创建辅助函数,但是该算法无法产生预期的结果,或者它实际上不是尾递归的。

这是功能:

    def game(boardState: BoardState,
             pieces: List[ChessPiece],
             acc: Set[BoardState]): Set[BoardState] = pieces match {
      case Nil => acc + boardState // No more pieces, boardState solved
      case x :: xs => getStates(boardState, x, xs, acc)
    }

    def getStates(boardState: BoardState,
                  piece: ChessPiece,
                  rest: List[ChessPiece],
                  acc: Set[BoardState]): Set[BoardState] = {
      // Ask if there are available squares
      if (boardState.availableSquares.nonEmpty) {
          // Get the states from every available square
        boardState.availableSquares.foldLeft(Set[BoardState]())((innerAcc, sqr) => {
          // Get the next chess piece
          val nextPiece = buildPiece(piece, sqr)
          // Check if placing the piece would result in an existing piece being attacked
          if (boardState.withPieces.forall(sqr => !nextPiece.isAttacking(sqr))) {
            // Do the recursion with the new Board State
            val newState = boardState.placePiece(nextPiece)
            innerAcc ++ game(newState, rest, acc) //This is the part that is not tail recursive
          } else innerAcc
        })
      } else {
      // There are no available places, search ends here
        acc
      }
    }

提前感谢您的建议!

2 个答案:

答案 0 :(得分:2)

嗯,n <- 1e7呼叫ak <- function() { m2 <- cbind(rep(seq_along(test), lengths(test)), unlist(test)) testresult[m2] <- 1 } wfw <- function() { for (i in 1:length(test)) { testresult3[i, test[[i]]] <- 1 } } library(microbemchmark) microbenchmark(ak(), wfw(), unit = 'relative', times = 20L) #Unit: relative # expr min lq mean median uq max neval cld # ak() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 20 a # wfw() 1.946415 1.945528 1.927263 1.926645 1.910907 1.940207 20 b game()呼叫getStates()。这看起来像蹦床可以处理的事情。

这里尝试使用TailCalls from the Standard Library

getStates()

警告:在虚拟所有缺少的部分(game()import scala.util.control.TailCalls._ def game(boardState: BoardState, pieces: List[ChessPiece], acc: Set[BoardState]): TailRec[Set[BoardState]] = pieces match { case Nil => done(acc + boardState) // No more pieces, boardState solved case x :: xs => tailcall(getStates(boardState, x, xs, acc)) } def getStates(boardState: BoardState, piece: ChessPiece, rest: List[ChessPiece], acc: Set[BoardState]): TailRec[Set[BoardState]] = done{ // Ask if there are available squares if (boardState.availableSquares.nonEmpty) { // Get the states from every available square boardState.availableSquares.foldLeft(Set[BoardState]())((innerAcc, sqr) => { // Get the next chess piece val nextPiece = buildPiece(piece, sqr) // Check if placing the piece would result in an existing piece being attacked if (boardState.withPieces.forall(sqr => !nextPiece.isAttacking(sqr))) { // Do the recursion with the new Board State val newState = boardState.placePiece(nextPiece) innerAcc ++ tailcall(game(newState, rest, acc)).result } else innerAcc }) } else { // There are no available places, search ends here acc } } 等)后,我就可以编译它了,所以我实际上并没有尝试过运行 。下次,请发布足够的代码以使其成为minimal, complete, verifiable example

答案 1 :(得分:1)

scala尾递归优化需要三件事:

  1. 该函数必须是自递归的
  2. 该函数每次调用只能调用一次
  3. 自叫必须在尾巴位置

为了满足1.您需要将game的实现折叠为getStates

要满足2.难度更大,因为该foldLeft调用可能会导致多个递归调用。解决方案是将availableSquares列表传递给递归函数,并在每次调用中处理一个元素。这是最棘手的事情。

要满足3.您将需要使最终结果成为调用的参数,并在没有更多工作要做时返回该结果。进行递归调用时,会将新数据添加到结果中,然后将其与其他参数一起传递给

这只是解决方案的概述,但希望对您有所帮助。