Scala 使用堆栈手动将树递归转换为尾部递归

Scala 使用堆栈手动将树递归转换为尾部递归,scala,recursion,tail-recursion,topological-sort,Scala,Recursion,Tail Recursion,Topological Sort,我在拓扑排序上实现了一个变体(在顶部),它返回所有拓扑排序,而不仅仅是一个。我有一个树递归实现,我想使尾部递归。我不想使用蹦床,相反,我想模拟调用堆栈,如中所述 这是我的算法的树递归版本: import scalax.collection.Graph import scalax.collection.GraphPredef._ import scalax.collection.GraphEdge._ import scala.collection.Set def allTopologicalS

我在拓扑排序上实现了一个变体(在顶部),它返回所有拓扑排序,而不仅仅是一个。我有一个树递归实现,我想使尾部递归。我不想使用蹦床,相反,我想模拟调用堆栈,如中所述

这是我的算法的树递归版本:

import scalax.collection.Graph
import scalax.collection.GraphPredef._
import scalax.collection.GraphEdge._
import scala.collection.Set

def allTopologicalSorts[T](graph: Graph[T, DiEdge]): Unit = {
  val indegree: Map[graph.NodeT, Int] = graph.nodes.map(node => (node, node.inDegree)).toMap

  def isSource(node: graph.NodeT): Boolean = indegree.get(node).get == 0

  def getSources(): Set[graph.NodeT] = graph.nodes.filter(node => isSource(node))

  processSources(getSources(), indegree, List[graph.NodeT](), 0)

  def processSources(sources: Set[graph.NodeT], indegrees: Map[graph.NodeT, Int], topOrder: List[graph.NodeT], cnt: Int): Unit = {
    if (sources.nonEmpty) {
      // `sources` contain all the nodes we can pick
      // --> generate all possibilities
      for (src <- sources) {
        val newTopOrder = src :: topOrder
        var newSources = sources - src

        // Decrease the in-degree of all adjacent nodes
        var newIndegrees = indegrees
        for (adjacent <- src.diSuccessors) {
          val newIndeg = newIndegrees.get(adjacent).get - 1
          newIndegrees = newIndegrees.updated(adjacent, newIndeg)
          // If in-degree becomes zero, add to sources
          if (newIndeg == 0) {
            newSources = newSources + adjacent
          }
        }

        processSources(newSources, newIndegrees, newTopOrder, cnt + 1)
      }
    }
    else if (cnt != graph.nodes.size) {
      println("There is a cycle in the graph.")
    }
    else {
      println(topOrder.reverse)
    }
  }
}
哪个正确返回

  • 清单(2,7,4,5)
  • 清单(2、4、7、5)
  • 清单(2、4、5、7)
现在,我尝试通过手动保留堆栈来实现尾部递归版本

import scalax.collection.Graph
import scalax.collection.GraphPredef._
import scalax.collection.GraphEdge._
import scala.collection.Set

def allTopologicalSorts[T](graph: Graph[T, DiEdge]): Unit = { 
  val indegree: Map[graph.NodeT, Int] = graph.nodes.map(node => (node, node.inDegree)).toMap

  def isSource(node: graph.NodeT): Boolean = indegree.get(node).get == 0

  def getSources(): Set[graph.NodeT] = graph.nodes.filter(node => isSource(node))

  def processSources(sources: Set[graph.NodeT], indegrees: Map[graph.NodeT, Int]): Unit = {
    type Order = List[graph.NodeT]
    case class Frame(sources: List[graph.NodeT], indegrees: Map[graph.NodeT, Int], topOrder: Order, cnt: Int)

    def step(stack: List[Frame]): Unit = {
      stack match {
        case Frame(src :: rest, indegrees, topOrder, cnt) :: tail => {
          val onBacktrackingFrame = Frame(rest, indegrees, topOrder, cnt)

          // Process src now and remember to do the rest later
          val newTopOrder = src :: topOrder
          var newSources = rest

          // Decrease the in-degree of all adjacent nodes
          var newIndegrees = indegrees
          for (adjacent <- src.diSuccessors) {
            val newIndeg = newIndegrees.get(adjacent).get - 1
            newIndegrees = newIndegrees.updated(adjacent, newIndeg)
            // If in-degree becomes zero, add to sources
            if (newIndeg == 0) {
              newSources = adjacent :: newSources
            }
          }

          val recursionFrame = Frame(newSources, newIndegrees, newTopOrder, cnt + 1)
          step(recursionFrame :: onBacktrackingFrame :: tail)
        }
        case Frame(Nil, indegrees, topOrder, cnt) :: tail => {
          println(topOrder.reverse)
          step(tail)
        }
        case Nil =>
      }
    }

    step(List(Frame(sources.toList, indegrees, List[graph.NodeT](), 0)))
  }

  processSources(getSources(), indegree)
}
导入scalax.collection.Graph
导入scalax.collection.GraphPredef_
导入scalax.collection.GraphEdge_
导入scala.collection.Set
def allTopologicalSorts[T](图:图[T,DiEdge]):单位={
val indegree:Map[graph.NodeT,Int]=graph.nodes.Map(node=>(node,node.indegree)).toMap
def isSource(节点:graph.NodeT):Boolean=indegree.get(节点)。get==0
def getSources():Set[graph.NodeT]=graph.nodes.filter(节点=>isSource(节点))
def processSources(sources:Set[graph.NodeT],indegrees:Map[graph.NodeT,Int]):单位={
类型顺序=列表[graph.NodeT]
案例类框架(来源:List[graph.NodeT],indegrees:Map[graph.NodeT,Int],topOrder:Order,cnt:Int)
定义步骤(堆栈:列表[帧]):单位={
堆栈匹配{
案例框架(src::rest、indegrees、topOrder、cnt)::tail=>{
val onBacktrackingFrame=帧(静止、索引、拓扑顺序、cnt)
//现在处理src,记住以后再做其余的
val newtoorder=src::topoorder
var newSources=rest
//降低所有相邻节点的插入程度
var newIndegrees=indegrees
用于(相邻的){
println(拓扑顺序反向)
台阶(尾部)
}
案例无=>
}
}
步骤(List(Frame(sources.toList,indegrees,List[graph.NodeT](),0)))
}
processSources(getSources(),indegree)
}
但是,这不起作用,因为它会导致

  • 清单(2、4、5、7)
  • 清单(2、4、5)
  • 清单(2、4、7)
  • 名单(2、4)
  • 名单(2、7)
  • 名单(2)
  • 列表()
堆栈有问题,但我找不到问题


相关问题:

此解决方案是尾部递归AFAICT,在我运行它时起作用,尽管我将其部分更改回了第一个版本,特别是将某些类型从
列表更改为
集,以保持对原始小版本的更改(我认为再次将其更改为
列表
应该是相对简单的):

def allTopologicalSortsNew[T](图:图[T,DiEdge]):单位={
类型顺序=列表[graph.NodeT]
案例类框架(源代码:Set[graph.NodeT],索引:Map[graph.NodeT,Int],拓扑序:Order,cnt:Int)
val indegree:Map[graph.NodeT,Int]=graph.nodes.Map(node=>(node,node.indegree)).toMap
def isSource(节点:graph.NodeT):Boolean=indegree.get(节点)。get==0
def getSources():Set[graph.NodeT]=graph.nodes.filter(节点=>isSource(节点))
def processSources(initialSources:Set[graph.NodeT],initialIndegrees:Map[graph.NodeT,Int]):单位={
定义步骤(堆栈:列表[帧]):单位={
堆栈匹配{
案例框架(源、索引、拓扑顺序、cnt)::如果!sources.isEmpty=>{
val futureFrames=for(src
}
}
步骤(列表(框架(initialSources,initialIndegrees,列表[graph.NodeT](),0)))
}
processSources(getSources(),indegree)
}
试试看

  def allTopologicalSorts[T](graph: Graph[T, DiEdge]): Stream[List[graph.NodeT]] = {
    val indegree: Map[graph.NodeT, Int] = graph.nodes.map(node => (node, node.inDegree)).toMap

    def isSource(node: graph.NodeT): Boolean = indegree.get(node).get == 0
    def getSources(): Set[graph.NodeT] = graph.nodes.filter(node => isSource(node))

    case class Frame(arg: Argument, parentArg: Option[Argument], res: Result)

    case class Argument(sources: Set[graph.NodeT], indegrees: Map[graph.NodeT, Int], topOrder: List[graph.NodeT], cnt: Int)

    sealed trait Result
    case object NotExpanded extends Result
    case object Expanded extends Result
    case class Calculated(value: Output) extends Result

    type Output = Stream[List[graph.NodeT]]

    // extract result from final state of stack
    def processSources(arg: Argument): Output =
      step1(List(Frame(arg, None, NotExpanded))) match {
        case Frame(`arg`, None, Calculated(res)) :: Nil => res
      }

    @tailrec
    // process stack as long as necessary
    def step1(stack: List[Frame]): List[Frame] = {
      val x = step(stack, Nil)
      x match {
        case Frame(arg, None, Calculated(res)) :: Nil => x
        case _ => step1(x)
      }
    }

    // helper method for handling "recursion backward" case in "step"
    def calcFromChildren(stack: List[Frame], parentArg: Argument): Option[(List[Frame], Frame)] = {
      val (childFrames, rest) = stack.span {
        case Frame(_, Some(`parentArg`), Calculated(_)) => true
        case _ => false
      }

      val output: Output = childFrames.map {
        case Frame(arg, Some(`parentArg`), Calculated(res)) => res
      }.toStream.flatten

      rest match {
        case Frame(`parentArg`, parentArg1, Expanded) :: rest1 if parentArg.sources.nonEmpty =>
          Some(rest1, Frame(parentArg, parentArg1, Calculated(output)))
        case _ => None
      }
    }

    @tailrec
    // process stack once
    def step(stack: List[Frame], acc: List[Frame]): List[Frame] = {
      stack match {
          // recursion backward
        case Frame(arg, Some(parentArg), Calculated(res)) :: frames if calcFromChildren(stack, parentArg).isDefined =>
          val (rest1, parentFrame) = calcFromChildren(stack, parentArg).get
          step(rest1, parentFrame :: acc)

          // base
        case Frame(arg, parentArg, _) :: frames if arg.sources.isEmpty && arg.cnt != graph.nodes.size =>
          throw new Error("There is a cycle in the graph.")
        case Frame(arg, parentArg, _) :: frames if arg.sources.isEmpty =>
          val res = arg.topOrder.reverse #:: Stream.empty[List[graph.NodeT]]
          step(frames, Frame(arg, parentArg, Calculated(res)) :: acc)

          // recursion forward
        case Frame(arg, parentArg, NotExpanded) :: frames =>

          val childFrames = arg.sources.toList.map(src => {
            val newTopOrder = src :: arg.topOrder
            var newSources = arg.sources - src

            var newIndegrees = arg.indegrees
            for (adjacent <- src.diSuccessors) {
              val newIndeg = newIndegrees.get(adjacent).get - 1
              newIndegrees = newIndegrees.updated(adjacent, newIndeg)
              if (newIndeg == 0) {
                newSources = newSources + adjacent
              }
            }

            val newArg = Argument(newSources, newIndegrees, newTopOrder, arg.cnt + 1)
            Frame(newArg, Some(arg), NotExpanded)
          })

          step(frames, Frame(arg, parentArg, Expanded) :: childFrames.reverse ::: acc)

          // ignore if not "recursion backward" case
        case Frame(arg, parentArg, Expanded) :: frames => step(frames, Frame(arg, parentArg, Expanded) :: acc)
        case Frame(arg, parentArg, Calculated(res)) :: frames => step(frames, Frame(arg, parentArg, Calculated(res)) :: acc)

          // stack is processed once
        case Nil => acc.reverse
      }
    }

    processSources(Argument(getSources(), indegree, List[graph.NodeT](), 0))
  }
def allTopologicalSorts[T](图形:图形[T,DiEdge]):流[List[graph.NodeT]={
val indegree:Map[graph.NodeT,Int]=graph.nodes.Map(node=>(node,node.indegree)).toMap
def isSource(节点:graph.NodeT):Boolean=indegree.get(节点)。get==0
def getSources():Set[graph.NodeT]=graph.nodes.filter(节点=>isSource(节点))
案例类框架(arg:Argument,parentArg:Option[Argument],res:Result)
case类参数(来源:Set[graph.NodeT],indegrees:Map[graph.NodeT,Int],topoorder:List[graph.NodeT],cnt:Int)
封闭性状结果
案例对象NotExpanded扩展结果
案例对象扩展结果
计算的案例类(值:输出)扩展结果
类型输出=流[List[graph.NodeT]]
//从堆栈的最终状态提取结果
def processSources(arg:参数):输出=
步骤1(列表(帧(arg,None,NotExpanded))匹配{
案例框架(`arg`,无,计算(res))::Nil=>res
}
@泰勒克
//根据需要处理堆栈
def步骤1(堆栈:列表[帧]):列表[帧]={
val x=阶跃(堆栈,零)
x匹配{
案例框架(arg,None,computed(res))::Nil=>x
案例=>步骤1(x)
}
}
//用于处理“步骤”中“递归向后”情况的帮助器方法
def calcFromChildren(堆栈:List[Frame],parentArg:Argument):选项[(List[Frame],Frame)]={
val(子帧,剩余)=stack.span{
大小写框(_,Some(`parentArg`),计算(_))=>true
大小写=>false
}
val输出:输出=childFrames.map{
案例框架(arg,Some(`parentArg`),计算(res))=>res
}.toStream.flatten
休息赛{
案例框架(`parentArg`,parentArg1,展开)::rest1如果parentArg.sources.nonEmpty=>
一些(rest1,帧(parentArg,parentArg1,计算(输出)))
案例=>无
}
}
@泰勒克
//处理堆栈一次
def步骤(堆栈:列表[帧]、附件:列表[帧]):列表[帧]={
堆栈匹配{
//向后递归
case Frame(arg,Some(parentArg),computed(res)):如果calcFromChildren(stack,parentArg),则为frames.isDefined=>
val(rest1,parentFrame)=calcFromChildren(stack,parentArg).get
步骤(rest1,父帧::acc)
//基地
案例框架(arg,parentArg,389;):如果arg.sources.isEmpty&&arg.cnt!=gr,则框架
  def allTopologicalSorts[T](graph: Graph[T, DiEdge]): Stream[List[graph.NodeT]] = {
    val indegree: Map[graph.NodeT, Int] = graph.nodes.map(node => (node, node.inDegree)).toMap

    def isSource(node: graph.NodeT): Boolean = indegree.get(node).get == 0
    def getSources(): Set[graph.NodeT] = graph.nodes.filter(node => isSource(node))

    case class Frame(arg: Argument, parentArg: Option[Argument], res: Result)

    case class Argument(sources: Set[graph.NodeT], indegrees: Map[graph.NodeT, Int], topOrder: List[graph.NodeT], cnt: Int)

    sealed trait Result
    case object NotExpanded extends Result
    case object Expanded extends Result
    case class Calculated(value: Output) extends Result

    type Output = Stream[List[graph.NodeT]]

    // extract result from final state of stack
    def processSources(arg: Argument): Output =
      step1(List(Frame(arg, None, NotExpanded))) match {
        case Frame(`arg`, None, Calculated(res)) :: Nil => res
      }

    @tailrec
    // process stack as long as necessary
    def step1(stack: List[Frame]): List[Frame] = {
      val x = step(stack, Nil)
      x match {
        case Frame(arg, None, Calculated(res)) :: Nil => x
        case _ => step1(x)
      }
    }

    // helper method for handling "recursion backward" case in "step"
    def calcFromChildren(stack: List[Frame], parentArg: Argument): Option[(List[Frame], Frame)] = {
      val (childFrames, rest) = stack.span {
        case Frame(_, Some(`parentArg`), Calculated(_)) => true
        case _ => false
      }

      val output: Output = childFrames.map {
        case Frame(arg, Some(`parentArg`), Calculated(res)) => res
      }.toStream.flatten

      rest match {
        case Frame(`parentArg`, parentArg1, Expanded) :: rest1 if parentArg.sources.nonEmpty =>
          Some(rest1, Frame(parentArg, parentArg1, Calculated(output)))
        case _ => None
      }
    }

    @tailrec
    // process stack once
    def step(stack: List[Frame], acc: List[Frame]): List[Frame] = {
      stack match {
          // recursion backward
        case Frame(arg, Some(parentArg), Calculated(res)) :: frames if calcFromChildren(stack, parentArg).isDefined =>
          val (rest1, parentFrame) = calcFromChildren(stack, parentArg).get
          step(rest1, parentFrame :: acc)

          // base
        case Frame(arg, parentArg, _) :: frames if arg.sources.isEmpty && arg.cnt != graph.nodes.size =>
          throw new Error("There is a cycle in the graph.")
        case Frame(arg, parentArg, _) :: frames if arg.sources.isEmpty =>
          val res = arg.topOrder.reverse #:: Stream.empty[List[graph.NodeT]]
          step(frames, Frame(arg, parentArg, Calculated(res)) :: acc)

          // recursion forward
        case Frame(arg, parentArg, NotExpanded) :: frames =>

          val childFrames = arg.sources.toList.map(src => {
            val newTopOrder = src :: arg.topOrder
            var newSources = arg.sources - src

            var newIndegrees = arg.indegrees
            for (adjacent <- src.diSuccessors) {
              val newIndeg = newIndegrees.get(adjacent).get - 1
              newIndegrees = newIndegrees.updated(adjacent, newIndeg)
              if (newIndeg == 0) {
                newSources = newSources + adjacent
              }
            }

            val newArg = Argument(newSources, newIndegrees, newTopOrder, arg.cnt + 1)
            Frame(newArg, Some(arg), NotExpanded)
          })

          step(frames, Frame(arg, parentArg, Expanded) :: childFrames.reverse ::: acc)

          // ignore if not "recursion backward" case
        case Frame(arg, parentArg, Expanded) :: frames => step(frames, Frame(arg, parentArg, Expanded) :: acc)
        case Frame(arg, parentArg, Calculated(res)) :: frames => step(frames, Frame(arg, parentArg, Calculated(res)) :: acc)

          // stack is processed once
        case Nil => acc.reverse
      }
    }

    processSources(Argument(getSources(), indegree, List[graph.NodeT](), 0))
  }