使用yield时使scala函数尾部递归?

使用yield时使scala函数尾部递归?,scala,recursion,tail-recursion,Scala,Recursion,Tail Recursion,我有一个功能正在工作 def iterate(start: Seq[TestObj], sources: Seq[TestObj]): Seq[TestSeq] = { val currentTotal = start.foldLeft(TestObj(0, 0, 0))(_ + _) (for (s: TestObj <- sources) yield { val newTotal = currentTotal + s if (newTotal < targ

我有一个功能正在工作

def iterate(start: Seq[TestObj], sources: Seq[TestObj]): Seq[TestSeq] = {
  val currentTotal = start.foldLeft(TestObj(0, 0, 0))(_ + _)
  (for (s: TestObj <- sources) yield {
    val newTotal = currentTotal + s
    if (newTotal < target) {
      iterate(start :+ s, sources)
    } else {
      Seq(TestSeq(newTotal, target delta newTotal, (start :+ s).sortBy(e => (e.x, e.y, e.z))))
    }
  }).flatten
}
def iterate(start:Seq[TestObj],sources:Seq[TestObj]):Seq[TestSeq]={
val currentTotal=start.foldLeft(TestObj(0,0,0))(uu+u)
(适用于(s:TestObj(e.x,e.y,e.z)))
}
}).压扁
}
但我想让这条尾巴递归。在我的一生中,我无法理解在保持for/yield功能的同时,如何进行尾部递归。。这里的FP向导能给我指出正确的方向吗

更新:感谢下面的评论,我取得了一些进展。我的新测试线束如下所示。但它还是爆炸了。。任何优化的提示都是受欢迎的,我很困惑

case class TestObj(x: Int, y: Int, z: Int) {
  def +(that: TestObj): TestObj = {
    new TestObj(this.x + that.x, this.y + that.y, this.z + that.z)
  }

  def <(that: TestObj): Boolean = {
    this.x < that.x && this.y < that.y && this.z < that.z
  }

  def delta(that: TestObj) = {
    math.abs(this.x - that.x) + math.abs(this.y - that.y) + math.abs(this.z - that.z)
  }
}

case class TestSeq(score: TestObj, delta: Int, sequence: Seq[TestObj])

val sources = Seq(
  new TestObj(1, 2, 3), new TestObj(2, 3, 4), new TestObj(3, 4, 5),
  new TestObj(2, 3, 4), new TestObj(3, 4, 5), new TestObj(4, 5, 6),
  new TestObj(3, 4, 5), new TestObj(4, 5, 6), new TestObj(5, 6, 7))
val target = new TestObj(50, 60, 70)

def iterate(start: Seq[TestObj], maxItems: Int, sources: Seq[TestObj]): Set[TestSeq] = {
  val currentTotal = start.foldLeft(TestObj(0, 0, 0))(_ + _)

  if(maxItems == 0) {
    return Set(TestSeq(currentTotal, target delta currentTotal, start.sortBy(f => (f.x,f.y,f.z))))
  }

  sources.flatMap(s => {
    val newTotal = currentTotal + s
    if (newTotal < target) {
      iterate(start :+ s, maxItems - 1, sources)
    } else {
      Set(TestSeq(newTotal, target delta newTotal, start :+ s))
    }
  }).toSet
}

val possibleCombinations = iterate(Seq(new TestObj(0, 0, 0)), 10, sources)
val usableCombinations = possibleCombinations.map(c => TestSeq(c.score, c.delta, c.sequence.drop(1)))

println(usableCombinations)
usableCombinations.toSeq.sortBy(f => f.delta).foreach(uc => {
  println(s"${uc.delta}: ${uc.sequence}")
})
case类TestObj(x:Int,y:Int,z:Int){
def+(that:TestObj):TestObj={
新的TestObj(this.x+that.x,this.y+that.y,this.z+that.z)
}
def(f.x、f.y、f.z)))
}
sources.flatMap(s=>{
val newTotal=当前总计+s
如果(新总数<目标){
迭代(开始:+s,maxItems-1,源)
}否则{
设置(TestSeq(新总数,目标增量新总数,开始:+s))
}
}).托塞特
}
val-possibleCombinations=iterate(Seq(newtestobj(0,0,0)),10,sources)
val usablecombines=possibleCombinations.map(c=>TestSeq(c.score,c.delta,c.sequence.drop(1)))
println(可用组合)
usablecombines.toSeq.sortBy(f=>f.delta).foreach(uc=>{
println(s“${uc.delta}:${uc.sequence}”)
})

您的函数似乎产生了许多相同的序列(我说的对吗?),这是有意的吗?是否要使用尾部递归使其运行更快?因为目前似乎存在比非尾部递归更糟糕的性能问题(重新计算每次调用的总数,将元素添加到末尾可能效率低下,构建整个序列而不是懒散地进行)。
iterate
无法将其转换为尾部递归函数。另外,
(对于(s)来说,不需要重复项——事实上,我稍后会将它们吹走,因此我欢迎更好的解决方案。尾部递归的主要原因是,当输入大量数据时,它目前会因内存不足而消失。我基本上是在尝试解决多变量背包的一种变体。给定几组整数(元组),如(1,2,3),找到所有源的组合,其中每个数字相加到目标。(或接近),即,(1,2,3)+(1,2,3)=(2,4,6)将与增量0匹配。(1,2,3)+(2,2,3)=(2,4,6)+(1增量)是的,问题不在于非尾部递归性,而在于结果的
Seq
。与结果大小相比,递归本身在这里并不太深。如果有足够的数据使堆栈大小成为问题,可能需要millenia进行计算,所以这不是问题。将重复项存储在res中ult大大增加了它的大小。我相信,你不能避免为多背包生成重复项,无论如何也不能用琐碎的算法。而且你不能让它变得懒惰,因为你必须过滤重复项。作为一种可能的解决方案,试着改变
Set[TestSeq]
的结果类型,而不是
Seq
(当然,交换
Seq)(TestSeq(newTotal…
line for
Set(…
)。这将首先避免存储重复项。此外,运行Java时不要忘记增加堆大小。这是一个NP问题,因此即使对于中等大小的输入,它也很快变得难以处理。您的函数似乎生成了许多相同的序列(我说的对吗?)当前的。
迭代
不能转换为尾部递归函数(s)不需要重复项——事实上,我稍后会把它们吹走,所以我欢迎一个更好的解决方案。尾部递归的主要原因是,当输入大量数据时,它会因内存不足而消失。我基本上是在尝试解决多变量背包的一个变体。给定几组整数(元组),如(1,2,3),找到所有源的组合,其中每个数字相加到目标。(或接近),即,(1,2,3)+(1,2,3)=(2,4,6)将与增量0匹配。(1,2,3)+(2,2,3)=(2,4,6)+(1增量)是的,问题不在于非尾部递归性,而在于结果的
Seq
。与结果大小相比,递归本身在这里并不太深。如果有足够的数据使堆栈大小成为问题,可能需要millenia进行计算,所以这不是问题。将重复项存储在res中ult大大增加了它的大小。我相信,你不能避免为多背包生成重复项,无论如何也不能用琐碎的算法。而且你不能让它变得懒惰,因为你必须过滤重复项。作为一种可能的解决方案,试着改变
Set[TestSeq]
的结果类型,而不是
Seq
(当然,交换
Seq)(TestSeq(newTotal…
line for
Set(…
)。这将首先避免存储重复项。此外,运行Java时不要忘记增加堆大小。这是一个NP问题,因此即使对于中等大小的输入,也很快变得棘手。