【问题标题】:Tail-Recursion for Towers of Hanoi in ScalaScala中河内塔的尾递归
【发布时间】:2013-10-09 05:40:20
【问题描述】:

我是 Scala 编程新手。 我的目标是为河内塔问题实现一个尾递归程序。 我相信它可以通过这样的递归来实现:

// Implementing a recursive function for Towers of Hanoi,where the no of disks is taken as 'n', 'from' being the Start Peg, 'to' being the End Peg, and 'via' being Intermediate Peg

def move(n: Int, from: Int, to: Int, via: Int) : Unit = { // Recursive Function
    if (n == 1) {  
     Console.println("Move disk from pole " + from + " to pole " + to)// each move iteration printed.
 }
 else {  
    move(n - 1, from, via, to) //Moving n-1 disks from source to Intermediate
    move(1, from, to, via) // Printing all the move iterations
    move(n - 1, via, to, from) //Moving n and other n-1 disks to the final destination
  } 
}  

也可以使用尾递归来实现吗?

【问题讨论】:

  • 我认为没有一种简单的方法可以使尾部递归。尾递归本质上与迭代循环相同。

标签: scala tail-recursion towers-of-hanoi


【解决方案1】:

有一种方法,将堆栈移动到移动的参数。像这样的:

case class Pos(n: Int, from: Int, to: Int, via: Int)

def move(pos: Pos, rest: List[Pos]) : Unit = {
  val Pos(n, from, to, via) = pos
  if (n == 1) {
    println(s"Move disk from pole $from to pole $to")
    move(rest.head, rest.tail)
  } else {
    val pos1 = Pos(n - 1, from, via, to)
    val pos2 = Pos(1, from, to, via)
    val pos3 = Pos(n - 1, via, to from)
    move(pos1, pos2 :: pos3 :: rest)
  }
} 

这通常是从不是自然尾递归的东西中获取尾递归的最简单方法,因为您只需要弄清楚堆栈上的内容。不幸的是,如果涉及到除了递归之外的操作,这可能会困难得多。

在 Scala 中解决问题的另一种方法是利用非严格性,例如Stream。应该是这样的:

def move(n: Int, from: Int, to: Int, via: Int): Stream[String] = {
  if (n == 1) Stream(s"Move disk from pole $from to pole $to")
  else {
    println(s"$n pins left")
    move(n - 1, from, via, to) #::: move(1, from, to, via) #::: move(n - 1, via, to, from)
  }
}

然后你只需打印move 返回的字符串。这不是尾递归,但这里的技巧是只有第一个 move 被评估——其他的被保存为函数,并且只根据需要进行评估。一个合适的 Stream(我认为 Scalaz 有一个)甚至不会评估。

【讨论】:

    【解决方案2】:

    Daniel 的出色解决方案提醒我,有一种更通用的方法可以使方法尾递归。您可以使用延续传递样式http://en.wikipedia.org/wiki/Continuation-passing_style

    基本上,如果您不想立即执行方法的其余部分,您可以将其包装在一个函数对象中并在以后运行它。在这种情况下,该函数称为延续。

    但是我不确定您是否可以将我的代码视为真正的尾递归。毕竟我必须定义第二种方法让编译器接受它。

    import scala.annotation.tailrec
    
    def move2(n: Int, from: Int, to: Int, via: Int)(c: => Unit) : Unit = move(n, from, to, via)(c)
    
    @tailrec
    def move(n: Int, from: Int, to: Int, via: Int)(c: => Unit) : Unit = {
      if (n == 1) {
        Console.println("Move disk from pole " + from + " to pole " + to)
        c
      } else {  
        move(n - 1, from, via, to) { // this is like creating a list made up of two continuations
          move2(1, from, to, via) {       // 1. this
            move2(n - 1, via, to, from) { // expression
            }                             // here
          }
          c // and 2. the previous continuation
        }
      }
    }
    
    move(3, 1, 3, 2){}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-11
      • 1970-01-01
      • 2013-08-28
      • 1970-01-01
      • 2021-07-06
      • 2017-02-04
      • 2016-01-27
      • 1970-01-01
      相关资源
      最近更新 更多