Reputation: 1235
What are some ideas for expressing this function in 'idiomatic' Scala. Or more precisely, is there a way to remove the local vars without sacrificing readability?
def solve(threshold: Int)(f: Int => Int): Int = {
var sum = 0
var curr = 0
while(sum < threshold) {
sum += f(curr)
curr += 1
}
curr
}
The only thing I could come up with was this, but it's longer and less readable in my opinion.
def solve2(threshold: Int)(f: Int => Int): Int = {
val resultIterator = Iterator.iterate (0, 0) { case (curr, sum) =>
(curr + 1, sum + f(curr))
}
(resultIterator find (_._2 >= threshold)).get._1
}
Upvotes: 9
Views: 450
Reputation: 53705
Here is how I would do it in Haskell:
solve threshold f = snd $ until test step (0, 0)
where test (sum, i) = sum >= threshold
step (sum, i) = (sum + f i, succ i)
This clearly marks the test
, the step
, and the initial values, just like the imperative version. I am not sure if scala has until
in the libs somewhere, but it is trivial to define:
def until[A](test: A => Boolean)(f: A => A)(v: A): A = {
if (test(v)) {
v
} else {
until(test)(f)(f(v))
}
}
def solve(threshold: Int)(f: Int => Int): Int = {
def test = (sum: Int, i: Int) => sum >= threshold
def step = (sum: Int, i: Int) => (sum + f(i), i + 1)
until(test.tupled)(step.tupled)((0, 0))._2
}
Upvotes: 4
Reputation: 1918
I always wonder when people talk about 'idiomatic' scala. Because in my opinion everyone has his own perception of idiomatic. If you are looking for a functional solution I would like to suggest you to take a look at the 'essence of the iterator pattern' . There is actually a very good blogpost in scala about this check it out here : http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html
Upvotes: 1
Reputation: 167901
You could
def solve(threshold: Int, i: Int = 0)(f: Int => Int) = {
if (threshold <= 0) i else solve(threshold - f(i), i+1)(f)
}
but I'm not sure that's actually clearer. Note that it's actually more characters than a compact version of the while loop:
def solve(threshold: Int)(f: Int => Int) = {
var s,i = 0; while (s < threshold) { s += f(i); i += 1 }; i
}
Loops with mutable variables aren't always bad, "idiomatic" or no. Just keep the mutable state safely contained within the function, and all anyone else sees is a stateless function to call.
Incidentally, although sum
is a sensible variable name, curr
is questionable. What's wrong with i
? It's widely used as an index variable, and anyway, having a variable at all is a nuisance; the point is you take something and increment it, whatever it is, by one step each time, and then return it. It is this flow of logic, not the name, which tells you (and others) what it is for.
Upvotes: 7
Reputation: 92066
def solve(threshold: Int)(f: Int => Int): Int = {
Iterator.from(0).map(f).scanLeft(0)(_ + _).indexWhere(threshold <=)
}
In my opinion, the loop version is much clearer.
Upvotes: 13
Reputation: 13137
The most direct approach is to turn the while loop into a nested tail-recursive function.
def solve(threshold: Int)(f: Int => Int): Int = {
def solveLoop(sum: Int, curr: Int): Int = if (sum < threshold) {
solveLoop(sum + f(curr), curr + 1)
} else {
curr
}
solveLoop(0,0)
}
This is the standard "functional" way of looping.
Upvotes: 10