We can write an imperative solution for Longest Increasing Subarray like this :
def findLengthOfLCIS(nums: Array[Int]): Int = {
var len = 0
var left = 0
for (i <- nums.indices) {
if (i > 0 && nums(i) <= nums(i - 1)) left = i
len = math.max(len, i - left + 1)
}
len
}
Now I want to write a functional solution without loops and indices that may return also all increasing subarrays.
def findLengthOfLCIS(nums: Array[Int]): Int = {
@annotation.tailrec
def spanIncreasing(xs: List[Int], result: List[Int]): (List[Int], List[Int]) = xs match {
case x :: tail if result.isEmpty || x > result.head => spanIncreasing(tail, x :: result)
case _ => (result.reverse, xs)
}
@annotation.tailrec
def dropNotIncreasing(xs: List[Int]): List[Int] = xs match {
case x :: y :: tail if x >= y => dropNotIncreasing(y :: tail)
case _ => xs
}
def step(pair: (List[Int], List[Int])): (List[Int], List[Int]) = {
spanIncreasing(pair._1, Nil) match {
case (increasing, tmp) => dropNotIncreasing(tmp) -> increasing
}
}
def increasingSubarrays(xs: List[Int]): Iterator[List[Int]] = {
if (xs.isEmpty) Iterator(Nil) else
Iterator.iterate(xs -> List.empty[Int])(step).drop(1).map(_._2).takeWhile(_.nonEmpty)
}
increasingSubarrays(nums.toList).map(_.size).max
}
The solution is working but looks monstrous. I guess using fold
or unfold
instead of iterate
will not simplify the solution significantly ... How would you simplify it ?
Is this fp enough?
def findLengthOfLCIS(nums: Array[Int]): Int =
(nums zip nums.tail).scanLeft(1){
case(r, (p, n)) => if(n > p) r + 1 else 1
}.max
Or
def findLengthOfLCIS(nums: Array[Int]): Int =
(nums zip nums.tail).foldLeft((1, 1)){
case((r, m), (p, n)) => if(n > p) (r+1, m.max(r+1)) else (1, m)
}._2
BTW: If fp style means tedious, I do not want it at all.
Note: The above solutions assuming that nums
is not empty, just as the problem Longest Increasing Subarray says. But it is easy to make it works for empty array, and keep one line.