How to tell the Scala compiler that a while loop will return a value? - scala

Some algorithms execute a while loop with condition true and will (for sure) end at some point with a return statement inside the body of the while loop. E.g.:
def foo: Int = {
while(true) {
// At some time, the while loop will do a return statement inside its body
if( ... )
return 0
}
}
Simple example (without semantic sense):
def foo: Int = {
var i = 0
while(true) {
i += 1
if(i == 10)
return 0
}
}
The Scala compiler complains about a type mismatch, because the while loop has type Unit and the compiler does not know, that the while loop will at some point return a value. We could fix this with a workaround like:
def foo: Int = {
var i = 0
while(true) {
i += 1
if(i == 10)
return 0
}
0 // !
}
But this looks ugly. Is there a better workaround ? Or even a better solution for this kind of problem ?

You could throw an exception:
def foo: Int = {
var i = 0
while(true) {
i += 1
if(i == 10)
return 0
}
throw new IllegalStateException("This should never happen")
}
The compiler will stop complaining about the type mismatch, and since the while loop always returns something, the exception will never be thrown. And if it is, you will quickly find out where you did something wrong :).
There are other ways to write this loop which are more idomatic and Scala-esque, but given the code you provided, this will get the job done in a clear and simple way.

Maybe you should just use tail recursion instead. It should end up compiling down to very similar bytecode:
import scala.annotation.tailrec
def foo: Int = {
#tailrec def bar(i: Int): Int = {
val j = i + 1
if (j == 10) return 0
else bar(j)
}
bar(0)
}
You might even want to make use of the default parameter value support:
#tailrec def foo(i: Int = 0): Int = {
val j = i + 1
if (j == 10) return 0
else foo(j)
}
Note that this way requires you to call the function as foo() not foo since it has an argument.

A more idiomatic way would be to use recursion. Something like this:
def foo: Int = {
import scala.annotation.tailrec
#tailrec def whileUnderTen(i: Int):Int = if ( i < 10) whileUnderTen(i+1) else 0
whileUnderTen(0)
}

For just these occasions, I have a "forever" construct defined in my personal standard library.
forever{
}
is in all ways equivalent to
while(true){
}
except that forever has type Nothing while the equivalent while construct has type Unit. Just one of those small extension capabilities that makes Scala such a joy.

Related

How do you print an integer value within an object in scala?

object perMissing {
def solution(A: Array[Int]): Int = {
def findMissing(i: Int, L: List[Int]): Int = {
if (L.isEmpty || L.head != i+1) {
i+1
println(i+1)}
else findMissing(i+1, L.tail)
}
if (A.length == 0) 1
else findMissing(0, A.toList.sorted)
}
solution(Array(2,3,1,5))
}
I'm new to the world of Scala. I come from Python and C world.
How do we print an integer value, eg. for debugging? For instance, if I want to see the value of i in every iteration.
I compile my code using scalac and run it using scala.
According to the signature of your findMissing function, it should return an Int. However, if you look at the implementation of that function, only one of the code paths (namely the else part) returns an Int - the if part on the other hand does not return anything (besides Unit), since the call to println is the last line of that particular code block. To fix this issue, just return the increased value by putting it at the end of the block:
def findMissing(i: Int, l: List[Int]): Int = {
val inc = i + 1
if (l.isEmpty || l.head != inc) {
println(inc)
inc
}
else findMissing(inc, l.tail)
}
Since findMissing is tail recursive, you could additionally annotate it with #tailrec to ensure it will be compiled with tail call optimization.

Ternary Operators in Scala

I would like to simplify this:
var countA: Int = 0
var countB: Int = 0
if (validItem) {
if (region.equalsIgnoreCase( "US" )) {
if (itemList > 0) {
countB = 1
} else {
countA = 1
}
} else {
countB = 1
}
} else {
countA = 1
}
How do I use ternary operator in scala.
You should not need to use a ternary operator in Scala. In Scala, if is an expression not a statement, you can say val x = if (b) 1 else 2.
The usage of var in your example also points to a problem, because you can usually avoid this when you use the if as an expression.
Let's try to break down the code to avoid the var, i.e. first remove all if statements that are not expressions with a corresponding else and always provide both values:
var countA: Int = ???
var countB: Int = ???
if (validItem) {
if (region.equalsIgnoreCase("US")) {
if (itemList > 0) {
countA = 0
countB = 1
} else {
countA = 1
countB = 0
}
} else {
countA = 0
countB = 1
}
} else {
countA = 1
countB = 0
}
Now we can define the condition for which either of countA and countB is one:
val isUS = region.equalsIgnoreCase("US")
val hasItems = itemList > 0
val isA = !validItem || (isUS && !hasItems)
val isB = !isA
// or: val isB = validItem && (!isUS || hasItems)
and then:
val countA = if (isA) 1 else 0
val countB = if (isB) 1 else 0
I think the short answer is that in Scala there is no ?: ternary operator. Although you can imitate the syntax using implicits (see #jwvh's answer), I think it doesn't really simplify anything.
There are a couple of important properties of the conventional ?:
it always has two branches
following from the previous property, the ternary operator always returns a value (this is mostly the point of using ?:)
val result: Int = if (true) 1 else 2
// result is 1
branches are evaluated lazily
if (true) 1 else (0/0) // returns 1
if (false) 0/0 else 2 // returns 2
// i.e. 0/0 is not evaluated
As you see, in Scala if-else (with else) construction satisfies these properties. This is not the case for if-else construction in some other languages, like C or Java, because it doesn't return a value.
So the bottom line is that in Scala you don't need a ternary operator, because you can just use if-else.
UPDATE
As Alexey Romanov mentions in the comments, if statement without else actually satisfies the first condition as well. When you write
val result = if (true) 1
it actually means if (true) 1 else (), so result will have type AnyVal instead of Int, because the return type of the if expression is the lowest common bound of the both branches (Int and Unit in this case).
This might be a bit confusing for a "newbie", but you could attach a ternary method to the Boolean class like so.
implicit class Ternary[T](condition: Boolean) {
def ??(a: => T, b: => T): T = if (condition) a else b
}
Usage:
(4 == 4)??("yes","no") // res0: String = yes
("abc".length < 2).??(1,0) // res1: Int = 0
List('c').isEmpty.??('X','+') // res2: Char = +
To expand on #0__'s answer (if that is his/her real name), you can also use tuples to assign to two variables at once.
val (countA, countB) =
if (validItem) {
if (region.equalsIgnoreCase("US")) {
if (itemList > 0) (0,1) else (1,0)
} else {
(0,1)
}
} else {
(1,0)
}
This is an old question and I'm taking a break from a current problem, so I thought I'd leave a basic pattern match solution.
val (countA, countB) =
(validItem, region.equalsIgnoreCase("US"), itemList > 0) match {
case (true, true, false) | (false, _, _) => (1, 0)
case _ => (0, 1)
}

Why is my function returning a Unit instead of Int?

I am new to Scala and working on a project. I am writing a function that is suppose to return the smallest Int in an array. However when I run it I get a type error that it is returning unit instead of int.
Here is my code:
def minWhile(r: Array[Int]): Int = {
var pos = 1
var minInt = r(0)
while (pos < r.length) {
if (r(pos) < minInt)
minInt = r(pos)
pos += 1
}
minInt
}
Thank you very much!
Your code is compiled correctly, because it always returns correct type Int. But it may cause some runtime exceptions, if we passed empty array into your function: minWhile(Array()).
def minWhile(r: Array[Int]): Int =
{
var pos = 1
var minInt = r(0) /* potential runtime exception */
while( pos < r.length){
if(r(pos) < minInt)
minInt = r(pos)
pos+=1
}
minInt
}
You have to check arrays bounds working with it.
Or you can use a shorter way:
def minWhile(r: Array[Int]): Option[Int] = if (r.nonEmpty) Some(r.min) else None
Your code should be compiled correctly but it may rise an exception when passed empty container. You are using variables which is discouraged.
I would do it using some recursion calls or scala collections API like so:
array.reduceLeft(a: Somehitng, b: Something => Something)
Check thia link:
http://m.alvinalexander.com/scala/scala-use-reduceleft-get-max-min-from-collection
Consider this more functional style of conveying the semantics of minWhile, as follows,
def minWhile(r: Array[Int]): Int = {
(r zip r.drop(1)).takeWhile(t => t._1 > t._2).last._2
}
where we zip consecutive items and take those monotonically decreasing. The desired result is found in the second part of the last duple.

Infinite loop seems to confuse Scala's type system

Here is an artificial toy example that demonstrates my problem:
def sscce(): Int = {
val rand = new Random()
var count = 0
while (true) { // type mismatch; found: Unit, required: Int
count += 1
if (rand.nextInt() == 42) return count
}
}
How can I help the compiler understand that this method will always return an Int?
I know the above toy example could easily be refactored to get rid of the infinite loop altogether, but I really want to have the infinite loop in my actual code. Trust me on this ;)
Always return an Int:
def sscce(): Int = {
val rand = new Random()
var count = 0
while (true) {
count += 1
if (rand.nextInt() == 42) return count
}
count // <-- this
}
You can also do:
def foo: Int = {
...
while(true) {
... return ...
}
throw new IllegalStateException // unreachable
}
this will typecheck because the type of the throw is Nothing, which is a subtype of Int.
See this question. While loops don't return a value. i.e. they return Unit which is the last statement in your function. So, the definition says it returns an Int but it actually returns Unit thus the type error. #ionut's answer fixes the type error by returning count as the last statement or here is a recursive approach.
def sscce(): Int = {
val rand = new Random()
def ssccer(count: Int): Int = {
if(rand.nextInt == 42) return count
else ssccer(count + 1)
}
ssccer(0)
}
Per the SLS, a while loop is executed similarly to:
def whileLoop(cond: => Boolean)(body: => Unit): Unit =
if (cond) { body ; whileLoop(cond)(body) } else {}
ie., it returns Unit. So the compiler sees the while as the last statement in sscce(), and therefore assumes that you're trying to return Unit. I don't think it's smart enough to realize that return count will eventually always return an Int.
The simple solution is to follow the suggestion of #Brian or #IonutGStan, and force it to return count, whether it truly needs it or not.
From a code quality standpoint, it would be good to ditch the while(true) loop and replace it with something more readable. As a nice side effect, it also solves your problem:
def sscce(): Int = {
val rand = new Random()
var count = 1
while (rand.nextInt() != 42) {
count += 1
}
count
}

Incrementing 'i' in scala for loop by differing amounts depending on circumstance

I want to write a for loop in scala, but the counter should get incremented by more than one (the amount is variable) in some special cases.
You can do this with a combination of a filter and an external var. Here is an example:
var nextValidVal = 0
for (i <- 0 to 99; if i >= nextValidVal) {
var amountToSkip = 0
// Whatever this loop is for
nextValidVal = if (amountToSkip > 0) i + amountToSkip + 1 else nextValidVal
}
So in the main body of your loop, you can set amountToSkip to n according to your conditions. The next n values of i´s sequence will be skipped.
If your sequence is pulled from some other kind of sequence, you could do it like this
var skip = 0
for (o <- someCollection if { val res = skip == 0; skip = if (!res) skip - 1 else 0; res } ) {
// Do stuff
}
If you set skip to a positive value in the body of the loop, the next n elements of the sequence will be skipped.
Of course, this is terribly imperative and side-effecty. I would look for other ways to to this where ever possible, by mapping or filtering or folding the original sequence.
You could implement your own stream to reflect step, for example:
import scala.collection.immutable.Stream
import ForStream._
object Test {
def main(args: Array[String]): Unit = {
val range = 0 to 20 by 1 withVariableStep; // in case you like definition through range
//val range = ForStream(0,20,1) // direct definition
for (i<- range) {
println(s"i=$i")
range.step = range.step + 1
}
}
}
object ForStream{
implicit def toForStream(range: Range): ForStream = new ForStreamMaster(range.start, range.end,range.step)
def apply(head:Int, end:Int, step:Int) = new ForStreamMaster(head, end,step)
}
abstract class ForStream(override val head: Int, val end: Int, var step: Int) extends Stream[Int] {
override val tailDefined = false
override val isEmpty = head > end
def withVariableStep = this
}
class ForStreamMaster(_head: Int, _end: Int, _Step: Int) extends ForStream(_head, _end,_Step){
override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + step, end, step, this)
}
class ForStreamSlave(_head: Int, _end: Int, _step: Int, val master: ForStream) extends ForStream(_head, _end,_step){
override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + master.step, end, master.step, master)
}
This prints:
i=0
i=2
i=5
i=9
i=14
i=20
You can define ForStream from Range with implicits, or define it directly. But be carefull:
You are not iterating Range anymore!
Stream should be immutable, but step is mutable!
Also as #om-nom-nom noted, this might be better implemented with recursion
Why not use the do-while loop?
var x = 0;
do{
...something
if(condition){change x to something else}
else{something else}
x+=1
}while(some condition for x)