As mentioned in a comment, to takes an optional step argument. Alternatively, you can change the step with the by method; the following examples should give you an idea of how it works:
assert((1 to 3) == List(1, 2, 3))
assert((3 to 1) == List())
assert(3.to(1, -1) == List(3, 2, 1))
assert((3 to 1 by -1) == List(3, 2, 1))
assert(1.to(5, 2) == List(1, 3, 5))
assert((1 to 5 by 2) == List(1, 3, 5))
I had a look at the documentation for to and indeed it's not explicit in this regard.
As you already noticed, since multiplication is commutative, you can simply use the range growing from 1 to n. Notice that you can take advantage of this further to simplify your function as follows:
def computeFactorial(n: Int): Int =
1.to(n).foldLeft(1)(_ * _)
assert(computeFactorial(0) == 1)
assert(computeFactorial(5) == 120)
You can play around with this code here on Scastie.
As a final note, both your implementation and my suggestion have the problem of being defined for negative numbers, which the factorial is not. A way to express this explicitly is to use the Option type as in the following example:
def computeFactorial(n: Int): Option[Int] =
Option.when(n >= 0) { 1.to(n).foldLeft(1)(_ * _) }
assert(computeFactorial(-1) == None)
assert(computeFactorial(0) == Some(1))
assert(computeFactorial(5) == Some(120))
This code is also available on Scastie for you to play with.
Since the exercise required you use reduceLeft, this is an alternative, although I find foldLeft to be a better fit for this problem in particular (but of course, if it's about learning, experimenting is great).
def computeFactorial(n: Int): Option[Int] = {
if (n < 0) {
None
} else if (n == 0) {
Some(1)
} else {
Some((1 to n).reduceLeft(_ * _))
}
}
assert(computeFactorial(-1) == None)
assert(computeFactorial(0) == Some(1))
assert(computeFactorial(5) == Some(120))
(n to 1 by -1). Without the-1, the default value is 1.