Skip to content

Commit 53f81d1

Browse files
authored
Replace nondeterministic performantTraversables
The `performantTraversables` exercise has three problems: 1. It is not deterministic. People report (and I experienced it myself) that the result is sometime `true` and sometimes `false`. 2. You should not benchmark method calls like this. The JVM needs to warm up, there are a lot of optimizations going on. Use a microbenchmarking framework. 3. The comment states that you might receive a `StackOverflowError`. This is not true, as `reduceRight` is implemented based on `reduceLeft`, making it also tail recursive if `reduceLeft` is tail recursive. I suggest to replace the exercise with another one demonstrating the relationship between the left and the right operation. The new exercise is deterministic. Also I removed the wrong comment about the `StackOverflowError`.
1 parent 2c25b43 commit 53f81d1

File tree

1 file changed

+12
-16
lines changed

1 file changed

+12
-16
lines changed

src/main/scala/stdlib/Traversables.scala

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -511,23 +511,19 @@ object Traversables extends FlatSpec with Matchers with org.scalaexercises.defin
511511
intList.min should be(res3)
512512
}
513513

514-
/** You would choose `foldLeft`/`reduceLeft` or `foldRight`/`reduceRight` based on your mathematical goal. One other reason for deciding is performance - `foldLeft` generally has better performance since it uses tail recursion. This exercise will either work fine or you will receive a `StackOverflowError`:
514+
/** The naive recursive implementation of `reduceRight` is not tail recursive and would lead to a stack overflow if used on larger traversables.
515+
* However, `reduceLeft` can be implemented with tail recursion.
516+
*
517+
* To avoid the potential stack overflow with the naive implementation of `reduceRight` we can easily implement it based on `reduceLeft` by reverting the list and the inverting the reduce function.
518+
* The same applies for folding operations.
519+
*
520+
* There is also a `reduce` (and `fold`) available, which works exactly like `reduceLeft` (and `foldLeft`) and it should be the prefered method to call unless there is a strong reason to use `reduceRight` (or `foldRight`).
515521
*/
516-
def performantTraversables(res0: Boolean) {
517-
val MAX_SIZE = 1000000
518-
val reduceLeftStartTime = new java.util.Date
519-
(1 to MAX_SIZE) reduceLeft (_ + _)
520-
val reduceLeftEndTime = new java.util.Date
521-
522-
val reduceRightStartTime = new java.util.Date
523-
(1 to MAX_SIZE) reduceRight (_ + _)
524-
val reduceRightEndTime = new java.util.Date
525-
526-
val totalReduceLeftTime = reduceLeftEndTime.getTime - reduceLeftStartTime.getTime
527-
val totalReduceRightTime = reduceRightEndTime.getTime - reduceRightStartTime
528-
.getTime
529-
530-
(totalReduceRightTime > totalReduceLeftTime) should be(res0)
522+
def reduceRightAsReduceLeft(res0: Int, res1: Int, res2: Int) {
523+
val intList = List(5, 4, 3, 2, 1)
524+
intList.reduceRight((x, y) => x - y) should be(res0)
525+
intList.reverse.reduceLeft((x, y) => y - x) should be(res1)
526+
intList.reverse.reduce((x, y) => y - x) should be(res2)
531527
}
532528

533529
/** `transpose` will take a traversable of traversables and group them by their position in it's own traversable, e.g.:

0 commit comments

Comments
 (0)