DEV Community

Brandon Rozek
Brandon Rozek

Posted on • Originally published at brandonrozek.com on

Different Views of Fold and Their Combinations

Fold is a functional programming pattern that operates over some sequence with a binary operation and a starting value. There are two variants:

FoldLeft: Performs the binary operation from the left of the sequence to the right.

List(1, 2, 3, 4).foldLeft(0)(_ + _)
// ((((0 + 1) + 2) + 3) + 4)
// 10

Enter fullscreen mode Exit fullscreen mode

FoldRight: Performs the binary operation from the right of the sequence to the left.

List(1, 2, 3, 4).foldRight(0)(_ + _)
// (1 + (2 + (3 + (4 + 0))))
// 10

Enter fullscreen mode Exit fullscreen mode

The two fold variants only return the same solution if the function being applied is associative. Which is the case for integer addition above.

Definition and the Two Views

Definition of foldLeft within Scala:

def foldLeft[B](z: B)(op: (B, A) => B): B = this match {
  case seq: IndexedSeq[A @unchecked] => foldl(seq, 0, z, op)
  case _ =>
    var result = z
    val it = iterator
    while (it.hasNext) {
      result = op(result, it.next())
    }
    result
  }

def foldl[B](seq: IndexedSeq[A], start: Int, z: B, op: (B, A) => B): B = {
    @tailrec def loop(at: Int, end: Int, acc: B): B =
      if (at == end) acc
      else loop(at + 1, end, op(acc, seq(at)))
    loop(start, seq.length, z)
  }

Enter fullscreen mode Exit fullscreen mode

Notice that this includes both a recursive and iterative definition within one function! Since most people start off by learning loops, let’s focus on the iterative implementation.

var result = z
while (it.hasNext) {
    result = op(result, it.next())
}

Enter fullscreen mode Exit fullscreen mode

Combining Folds

When building out complex functions we may want to loop multiple times:

  • While loops next to each other ($n_1 + n_2$ complexity)
  • While loop within a while loop ($n_1 * n_2$ complexity)

$n_1 + n_2$ Complexity

The following is an example1 of this class of algorithms.

Given a list of words, title case each word and combine them into a single sentence.

val l1 = List("steve", "is", "doing", "great")
val title = (s: String) => s.substring(0, 1).toUpperCase + s.substring(1)

val it1 = l1.iterator
var id1 = List.empty[String]

while (it1.hasNext) {
    id1 = id1 :+ title(it1.next())
}

val it2 = id1.iterator
var id2 = ""
while (it2.hasNext) {
    id2 = id2 + " " + it2.next()
}

Enter fullscreen mode Exit fullscreen mode

Converting to fold solution:

val l1 = List("steve", "is", "doing", "great")
val title = (s: String) => s.substring(0, 1).toUpperCase + s.substring(1)
val id1 = List.empty[String]
val id2 = ""

l1.foldLeft(id1)((c, n) =>
    c :+ title(n)
).foldLeft(id2)((c, n) =>
    c + " " + n
)

Enter fullscreen mode Exit fullscreen mode

$n_1 * n_2$ Complexity

An example of this class of programs is the Cartesian product$$ (1, 2, 3) \times (4, 5,6) \rightarrow ((1,4), (1,5), (1,6), \dots, (3,4), (3,5), (3,6)) $$ Iterative Implementation:

val listInput1 = List(1,2,3)
val listInput2 = List(4,5,6)

var id_outer = List.empty[(Int, Int)]
val it1 = listInput1.iterator

while (it1.hasNext) {
    var id_inner = List.empty[(Int, Int)]
    val n1 = it1.next()
    val it2 = listInput2.iterator
    while (it2.hasNext) {
        val n2 = it2.next()
        result_inner = result_inner :+ (n1, n2)
    }
    result = result ++ result_inner
}

Enter fullscreen mode Exit fullscreen mode

Converting to fold solution:

val l1 = List(1,2,3)
val l2 = List(4,5,6)
val id_outer = List.empty[(Int, Int)]
val id_inner = List.empty[(Int, Int)]

l1.foldLeft(id_outer)((c1, n1) =>
    c1 ++ l2.foldLeft(id_inner)((c2, n2) =>
        c2 :+ (n1, n2)
    )
)

Enter fullscreen mode Exit fullscreen mode

  1. Thanks Clare for thinking of this example! ↩︎

Top comments (0)