Scala方法优化

Scala方法优化,scala,optimization,tail-recursion,Scala,Optimization,Tail Recursion,我有一个递归调用的def,我使用了一堆案例。 我想知道是否有什么好的解决方案可以在不损失定义可读性的情况下摆脱这种情况 @tailrec def getElements(existent:List[List[String]], proposed: List[List[String]], result: List[(String,List[String])]): List[(String, List[String])]= { proposed match { case

我有一个递归调用的def,我使用了一堆案例。 我想知道是否有什么好的解决方案可以在不损失定义可读性的情况下摆脱这种情况

 @tailrec
  def getElements(existent:List[List[String]], proposed: List[List[String]], result: List[(String,List[String])]): List[(String, List[String])]= {

    proposed match {
      case head :: tail => {
        existent.find(element => identicalOfMatch(element, head)) match {

          case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Same", elem) :: result)
          case None =>   {
            existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {

              case Some(elem) => getElements(existent.filterNot(e => e == elem), head::tail, ("Delete", elem) :: result)
              case None => {
                existent.find(element => firstOfMatch(element, head)) match {

                  case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)
                  case None => {
                    existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {

                      case Some(elem) =>  getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)

                      case None =>        getElements(Nil, Nil, existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: result)
                    }
                  }
                }
              }
            }
          }
        }
      }
      case Nil => result
    }
  }

如果您可以将方法拆分为多个方法,那么您的方法可读性将大大提高。 不幸的是,如果希望函数是尾部递归的,就不能这样做

但是有一个被调用的解决方案,它允许您创建一个任意深度的函数递归调用链,这是堆栈安全的

蹦床在Scala标准库中的包
Scala.util.control.TailCalls
中实现。我重新实现了你的方法来利用它

我做的第一件事是从
getElements
中删除累加器参数,我们不再需要它了

然后我将
getElements
拆分为三个函数。我调用了嵌套函数
ifNoneMatched
ifNoneMatched2
,但您可能会找到更有意义的名称

然后,我将链中对函数的每个调用包装为
tailcall
,将每个常量包装为
done
(在本例中为
Nil
)。当我需要向递归调用返回的列表中追加一些内容时,我使用了
TailRec
中的
flatMap

import scala.util.control.TailCalls._

def getElements(existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]]= {

    proposed match {
      case head :: tail => {
        existent.find(element => identicalOfMatch(element, head)) match {
          case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), tail).map(("Same", elem) :: _))
          case None => tailcall(ifNoneMatched(head, tail, existent, proposed))
        }
      }
      case Nil => done(Nil)
    }
}

def ifNoneMatched(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
    existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {

      case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), proposed)).map(("Delete", elem) :: _)
      case None => tailcall(ifNoneMatched2(head, tail, existent, proposed))
    }
}

def ifNoneMatched2(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
    existent.find(element => firstOfMatch(element, head)) match {

      case Some(elem) => getElements(existent.filterNot(e => e == elem), tail).map(("Update", head) :: _)
      case None => {
        existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {
          case Some(elem) =>  tailcall(getElements(existent.filterNot(e => e == elem), tail)).map(("Update", head) :: _)
          case None => getElements(Nil, Nil).map(existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: _)
        }
      }
    }
}
getElements
现在返回
TailRec[List[(String,List[String])]]
,但只需调用
result
即可将其展开

当然,您可以更深入地嵌套方法。在将方法调用包装到
tailcall
中之前,堆栈是安全的


我没有重新实现你的方法,比如
identicalOfMatch
等等,所以我无法真正测试我的实现是否有效。如果有东西坏了,请告诉我;)

试着将每个案例分成一个较小的函数。你能分享你的函数吗?或者至少是他们的签名?(
identicalOfMatch
noneOfMatch
canDelete
…)所有这些函数都是A=>Boolean或(A,B)=>Boolean,它们的职责是过滤掉一些案例谢谢,我会尝试一下,然后回来。最后一个问题:如果我经常使用2个集合来比较每个集合大约10.000个元素,是否存在堆栈溢出的危险?我只使用小集合进行过早的测试,因为整个工作流没有完全实现否,因为列表不是存储在堆栈上,而是存储在堆上。但是,当然,您可以有堆溢出,这将导致
java.lang.OutOfMemoryError
。实际上,它没有。它进入一个连续的过程loop@user2963757你能分享更多的代码(你的方法和输入数据)吗?也许我能找到答案。我只是用了你的例子,对所有情况进行了扩展,当列表遍历结束时,结果是一个done(NIl)。尝试调试小场景,但我发现这种新结构存在问题。无论如何,谢谢你,我真的很感激!