scaladata-structuressub-arrayfoldleftkadanes-algorithm

Kadane algorithm in Scala explanation


I am interested in learning how to implement the Kadane (maximum subarray sum) algorithm in scala with foldLeft function. I run through this example on stack overflow, however I am not sure I understand what the algorithm does exactly. This is how the algorithm looks like:

someArray.foldLeft(0 -> 0) {
      case ((maxUpToHere, maxSoFar), n) => val maxEndingHere = 0 max maxUpToHere + n
        maxEndingHere -> (maxEndingHere max maxSoFar)
    }._2

Is the content included in the {} the lambda function that needs to be applied on every element? and also what does this line do exactly maxEndingHere -> (maxEndingHere max maxSoFar)? Why are the brackets in the parenthesis separated by space? I appreciate any help, sorry if my question comes across as too ignorant, but I am new to Scala


Solution

  • First, you need to understand what foldLeft is. The meaning of this function is to fold over collection into a single value, by passing combining operation and initial element:

    // Think of applying op(op(op(…), z) from left to right:
    def foldLeft[B](z: B)(op: (B, A) ⇒ B): B
    

    Now, let's see what's happening in your foldLeft. First, the 0 -> 0 is passed. It means that the type B of the initial element is a tuple (Int, Int) with the value (0, 0).

    Second, the opening brackets define a function. In scala you can pass it with curly braces. So, the function expects arguments of (B, A) in our case the type B is a tuple (Int, Int) and the type A is the type of an Array elements which is Int.

    So, when you can translate your code like this:

    someArray.foldLeft(0 -> 0) {
      (tuple: (Int, Int), element: Int) => //the body
    }
    

    Now, in Scala you can create partial functions with case keyword by applying provided pattern. The pattern in our case matches the provided argument by binding the variables maxUpToHere and maxSoFar to the tuple elements and the n to the element of an array.

    So, the function will take each element from an array, apply it with the provided tuple and pass it to the next application until the array was processed fully. Now, let's see what's happening in function body:

    val maxEndingHere = 0 max maxUpToHere + n
    maxEndingHere -> (maxEndingHere max maxSoFar)
    

    Remember, that our function should return the next B to apply for invocation with element from an array. The B is a tuple in our case. So, the idea is to store the overall max and the local max of the sequence in a tuple.

    The maxEndingHere takes the max between 0 and the sum of the previous calculation with the current element of an array n. If the current element will be negative, it will reduce the max sequence hence produce 0 on the max comparison result, thus resetting the accumulated value.

    Then we just create new tuple with the new calculated sum of the sequence maxEndingHere and the maximum between current value and the one that is calculated so far (hence the name maxSoFar).

    And lastly, we just take the second value of the calculated tuple by calling ._2.