问题
I have the following code, but I can't get it to work. As soon as I place a while loop inside the case, it's returning a unit, no matter what I change within the brackets.
case While(c, body) =>
while (true) {
eval(Num(1))
}
}
How can I make this while loop return a non-Unit type?
I tried adding brackets around my while condition, but still it doesn't do what it's supposed to.
Any pointers?
Update
A little more background information since I didn't really explain what the code should do, which seems to be handy if I want to receive some help;
I have defined a eval(exp : Exp)
. This will evaluate a function.
Exp
is an abstract class. Extended by several classes like Plus
, Minus
(few more basic operations) and a IfThenElse(cond : Exp, then : Exp, else : Exp)
. Last but not least, there's the While(cond: Exp, body: Exp)
.
Example of how it should be used;
eval(Plus(Num(1),Num(4))
would result in NumValue(5)
. (Evaluation of Num(v : Value) results in NumValue(v). NumValue extends Value, which is another abstract class).
eval(While(Lt(Num(1),Var("n")), Plus(Num(1), Var("n"))))
Lt(a : Exp, b : Exp)
returns NumValue(1)
if a < b.
回答1:
It's probably clear from the other answer that Scala while
loops always return Unit
. What's nice about Scala is that if it doesn't do what you want, you can always extend it.
Here is the definition of a while
-like construct that returns the result of the last iteration (it will throw an exception if the loop is never entered):
def whiley[T](cond : =>Boolean)(body : =>T) : T = {
@scala.annotation.tailrec
def loop(previous : T) : T = if(cond) loop(body) else previous
if(cond) loop(body) else throw new Exception("Loop must be entered at least once.")
}
...and you can then use it as a while
. (In fact, the @tailrec
annotation will make it compile into the exact same thing as a while
loop.)
var x = 10
val atExit = whiley(x > 0) {
val squared = x * x
println(x)
x -= 1
squared
}
println("The last time x was printed, its square was : " + atExit)
(Note that I'm not claiming the construct is useful.)
回答2:
Which iteration would you expect this loop to return? If you want a Seq
of the results of all iterations, use a for
expression (also called for
comprehension). If you want just the last one, create a var
outside the loop, set its value on each iteration, and return that var
after the loop. (Also look into other looping constructs that are implemented as functions on different types of collections, like foldLeft
and foldRight
, which have their own interesting behaviors as far as return value goes.) The Scala while
loop returns Unit
because there's no sensible one size fits all answer to this question.
(By the way, there's no way for the compiler to know this, but the loop you wrote will never return. If the compiler could theoretically be smart enough to figure out that while(true)
never terminates, then the expected return type would be Nothing
.)
回答3:
The only purpose of a while loop is to execute a side-effect. Or put another way, it will always evaluate to Unit.
If you want something meaningful back, why don't you consider using an if-else-expression or a for-expression?
回答4:
As everyone else and their mothers said, while loops do not return values in Scala. What no one seems to have mentioned is that there's a reason for that: performance.
Returning a value has an impact on performance, so the compiler would have to be smart about when you do need that return value, and when you don't. There are cases where that can be trivially done, but there are complex cases as well. The compiler would have to be smarter, which means it would be slower and more complex. The cost was deemed not worth the benefit.
Now, there are two looping constructs in Scala (all the others are based on these two): while loops and recursion. Scala can optimize tail recursion, and the result is often faster than while loops. Or, otherwise, you can use while loops and get the result back through side effects.
来源:https://stackoverflow.com/questions/9785965/scala-while-loop-returns-unit-all-the-time