The methods are called unfoldRight and unfoldLeft because they are more or less the inverse of foldLeft and foldRight. foldLeft and Right run a function on each element in a list producing some result. unfoldLeft and right produce a list from a function and a seed value. The List is created from executing the function repeatedly with the result from the previous execution (or the seed value). Each function call returns a result which is the element in the list. The list is done when the function returns None rather than Some(x).
This sample is directly derived from the samples: http://paste.pocoo.org/show/140865/
Remember the performance differences between the two. Adding an element to the head of scala list is a constant operation but adding an element to the end(tail) of the list is linear time based on the size of the list. So unfoldLeft will typically suffer from worse performance.
- scala> def unfoldRight[A, B](seed: B)(f: B => Option[(A, B)]): List[A] = f(seed) match {
- | case Some((a, b)) => a :: unfoldRight(b)(f)
- | case None => Nil
- | }
- unfoldRight: [A,B](B)((B) => Option[(A, B)])List[A]
- scala> unfoldRight(10) { x => if (x == 0) None else Some((x, x - 1)) }
- res0: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
- scala> unfoldRight("Jesse") { x => if (x.length == 0) None else Some((x, x.drop(1).toString)) }
- res2: List[java.lang.String] = List(Jesse, esse, sse, se, e)
- scala> def unfoldLeft[A, B](seed: B)(f: B => Option[(B, A)]) = {
- | def loop(seed: B)(ls: List[A]): List[A] = f(seed) match {
- | case Some((b, a)) => loop(b)(a :: ls)
- | case None => ls
- | }
- |
- | loop(seed)(Nil)
- | }
- unfoldLeft: [A,B](B)((B) => Option[(B, A)])List[A]
- scala> unfoldLeft(1) { x => if (x == 11) None else Some((x + 1, x)) }
- res0: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
- // Notice that the result is ordered in the opposite way from unfoldRight
- // Also notice the order of the list is reversed since the algorithm is the same but
- // unfoldLeft adds to end of list.
- scala> unfoldLeft("Jesse") { x => if (x.length == 0) None else Some((x.drop(1).toString,x)) }
- res1: List[java.lang.String] = List(e, se, sse, esse, Jesse)
unfoldLeft looks to be implemented without adding an element to the end of a list. It looks pretty efficient. unfoldRight can blow the stack
ReplyDeleteShouldn't unfoldRight construct the list from right to left? They seem reversed to me.
ReplyDeleteThe name is ambiguous. When I wrote this post I viewed it as the resulting list flows left to right with regard to the elements coming from the function in the same way a stream flows.
ReplyDeleteBut I certainly understand where you are coming from and if I wrote it now I might have written it in that fashion as well.
@steshaw. Neither example is ideal but unfoldRight could be rewritten to be tail recursive.
ReplyDelete