Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala extending while loops to do-until expressions

Tags:

scala

I'm trying to do some experiment with Scala. I'd like to repeat this experiment (randomized) until the expected result comes out and get that result. If I do this with either while or do-while loop, then I need to write (suppose 'body' represents the experiment and 'cond' indicates if it's expected):

do {
  val result = body
} while(!cond(result))

It does not work, however, since the last condition cannot refer to local variables from the loop body. We need to modify this control abstraction a little bit like this:

def repeat[A](body: => A)(cond: A => Boolean): A = {
  val result = body
  if (cond(result)) result else repeat(body)(cond)
}

It works somehow but is not perfect for me since I need to call this method by passing two parameters, e.g.:

val result = repeat(body)(a => ...)

I'm wondering whether there is a more efficient and natural way to do this so that it looks more like a built-in structure:

val result = do { body } until (a => ...)

One excellent solution for body without a return value is found in this post: How Does One Make Scala Control Abstraction in Repeat Until?, the last one-liner answer. Its body part in that answer does not return a value, so the until can be a method of the new AnyRef object, but that trick does not apply here, since we want to return A rather than AnyRef. Is there any way to achieve this? Thanks.

like image 865
Yang Avatar asked Nov 25 '12 16:11

Yang


People also ask

Why is my while loop infinite?

Basically, the infinite loop happens when the condition in the while loop always evaluates to true. This can happen when the variables within the loop aren't updated correctly, or aren't updated at all.

How do you break a while loop in Scala?

In Scala, we use a break statement to break the execution of the loop in the program. Scala programing language does not contain any concept of break statement(in above 2.8 versions), instead of break statement, it provides a break method, which is used to break the execution of a program or a loop.

Is there continue in Scala?

Loop Control Statements As such Scala does not support break or continue statement like Java does but starting from Scala version 2.8, there is a way to break the loops.

Do while loop vs while loop?

do while loop is similar to while loop with the only difference that it checks for the condition after executing the statements, and therefore is an example of Exit Control Loop.


2 Answers

With a minor modification you can turn your current approach in a kind of mini fluent API, which results in a syntax that is close to what you want:

class run[A](body: => A) {
  def until(cond: A => Boolean): A = {
    val result = body
    if (cond(result)) result else until(cond)
  }
}
object run {
  def apply[A](body: => A) = new run(body)
}

Since do is a reserved word, we have to go with run. The result would now look like this:

run {
  // body with a result type A
} until (a => ...)

Edit:

I just realized that I almost reinvented what was already proposed in the linked question. One possibility to extend that approach to return a type A instead of Unit would be:

def repeat[A](body: => A) = new {
  def until(condition: A => Boolean): A = {
    var a = body
    while (!condition(a)) { a = body }
    a     
  }   
}
like image 26
bluenote10 Avatar answered Sep 28 '22 05:09

bluenote10


You're mixing programming styles and getting in trouble because of it.

Your loop is only good for heating up your processor unless you do some sort of side effect within it.

do {
  val result = bodyThatPrintsOrSomething
} until (!cond(result))

So, if you're going with side-effecting code, just put the condition into a var:

var result: Whatever = _
do {
  result = bodyThatPrintsOrSomething
} until (!cond(result))

or the equivalent:

var result = bodyThatPrintsOrSomething
while (!cond(result)) result = bodyThatPrintsOrSomething

Alternatively, if you take a functional approach, you're going to have to return the result of the computation anyway. Then use something like:

Iterator.continually{ bodyThatGivesAResult }.takeWhile(cond)

(there is a known annoyance of Iterator not doing a great job at taking all the good ones plus the first bad one in a list).

Or you can use your repeat method, which is tail-recursive. If you don't trust that it is, check the bytecode (with javap -c), add the @annotation.tailrec annotation so the compiler will throw an error if it is not tail-recursive, or write it as a while loop using the var method:

def repeat[A](body: => A)(cond: A => Boolean): A = {
  var a = body
  while (cond(a)) { a = body }
  a
}
like image 71
Rex Kerr Avatar answered Sep 28 '22 05:09

Rex Kerr