繁体   English   中英

如何在Scala中优雅地实现这个简单的算法

[英]How to implement this simple algorithm elegantly in Scala

我希望使用以下(或类似)签名来优雅地实现该方法:

def increasingSubsequences(xs: List[Int]): List[List[Int]]

它的作用是分割输入序列而不重新排序元素,以便结果中的每个子序列都严格增加。

我自己实现如下:

  def increasingSubsequences(list: List[Int], temp: List[Int] = Nil, res: List[List[Int]] = Nil): List[List[Int]] = {
    (list, temp) match {
      case (x :: xs, t :: ts) if t < x => increasingSubsequences(xs, x :: temp, res)
      case (x :: xs, Nil) => increasingSubsequences(xs, List(x), res)
      case _ if list.nonEmpty => increasingSubsequences(list, Nil, temp.reverse :: res)
      case _ if temp.nonEmpty => (temp.reverse :: res).reverse
      case _ => res.reverse
    }
  }

虽然上面的代码不是很长,但如果可能的话,我希望看到一个更优雅和简洁的解决方案(可能使用组合器)。

样本输入和输出:

List(5, 6, 2, 3, 4, 1, 2, 6, 8, 5) —> List(List(5, 6), List(2, 3, 4), List(1, 2, 6, 8), List(5))
List() —> List()
List(1, 2, 3, 4, 5) —> List(1, 2, 3, 4, 5)
List(5, 4, 3, 2, 1) —> List(List(5), List(4), List(3), List(2), List(1))

颠倒列表然后使用foldLeft

def increasingSubsequences(list: List[Int]) = list.reverse.foldLeft(List[List[Int]]()) {
  case (a :: as, b) if b < a.head => (b :: a) :: as   // same subsequence
  case (as, b)                    => List(b)  :: as   // new subsequence
}

使用scalaz的组时 ,它非常简单:

import scalaz.std.list._

def increasingSubsequences(xs: List[Int]) = groupWhen(xs)(_ < _)

我认为这符合您的要求。 它确实使用了一个可以重构为另一个匹配语句的if-else子句,但我不喜欢它看起来那样。 在不使用辅助方法的情况下,遗憾的是,我想不出一个很好的方法来使尾部递归。

def increasingSubsequences(xs: List[Int]): List[List[Int]] = {
  xs match {
    case Nil => List(Nil) //in case someone calls on empty list
    case (head :: Nil) => List(head :: Nil) //base case
    case (head :: rest) => {
      val finishedRest = increasingSubsequences(rest)
      finishedRest match {
        case ((headOfFirst :: restOfFirst) :: restOfFinished) => {
          if (head < headOfFirst) (head :: headOfFirst :: restOfFirst) :: restOfFinished
          else List(List(head), headOfFirst::restOfFirst) ++ restOfFinished
        }
      }
    }
  }
}

您指定的内容存在两个小差异:

  • List()生成List(List())
  • List(1, 2, 3, 4, 5)生成List(List(1, 2, 3, 4, 5)

我只能假设您打算以这种方式指定它们,否则它们不适合List[List[Int]]的返回类型。*

*我还应该提一下,第一个有点好,因为Scala不介意内部List[Int]被暗示,并且实际上产生了如果你将第一个案例改为Nil => Nil

我用List的foldLeft完成了这个:

def increasingSubsequences(list:List[Int]) =
  list.foldLeft(List[List[Int]]())((accum, value) => {
    accum match {
      case Nil => List(List(value))
      case headList :: tailList => {
        headList match {
          case head :: tail if value > head => List(headList :+ value) ++ tailList
          case _ => List(List(value)) ++ accum
        }
      }
    }
}).reverse

正如@Dimitri指出的那样,如果使用::map(._reverse) ,复杂性可能会更好。 所以,在这里你去...你可以决定:)

def increasingSubsequences(list:List[Int]) =
  list.foldLeft(List[List[Int]]())((accum, value) => {
    accum match {
      case Nil => List(List(value))
      case headList :: tailList => {
        headList match {
          case head :: tail if value > head => List(value :: headList) ++ tailList
          case _ => List(List(value)) ++ accum
        }
      }
    }
}).map(_.reverse).reverse
  def increasingSubsequences(xs: List[Int]): List[List[Int]] = {
    val splits = xs.sliding(2).zipWithIndex.toList
      .map { case (sub, i) => if (sub(0) >= sub(1)) i + 1 else -1 } filter (_ > 0)
    (List(0, splits.head) :: splits.sliding(2).toList ++ List(List(splits.last, xs.size)))
      .map { case pos => xs.slice(pos(0), pos(1)) }
  }

您可以先找到所有分割点,然后将它们分开。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM