Conditional loop conversion in Scala

I would like to convert a piece of Java code that looks like this: Scala:

for (Iterator<Task> it = tasks.iterator(); it.hasNext() && workflow.isAutoRun();) { Task task = it.next(); if (!runTask(task)) break; } 

I'm not a fan of scala for- concepts (I don't know how I understand iteration anyway), and I came up with the following:

 val completed = tasks.forall { task => workflow.isAutoRun && runTask(task) } 

However, the scaladoc for the forall method is as follows (italics mine):

Apply the predicate p to all elements of this iterable object and return true if the predicate returns true for all elements

This is not equivalent to what I did (because it implies that the predicate will be evaluated for each element, regardless of whether the previous estimate returned false ) and (in fact) actually not equivalent to what forall actually does, which on Iterator as follows:

  def forall(p: A => Boolean): Boolean = { var res = true while (res && hasNext) res = p(next) res } 

Anyway, I'm distracted: does anyone have any better suggestions for what the scala code should look like? I want to see something that better conveys intent:

tasks.doUntil(t => !isAutoRun || !runTask(t))

+4
source share
5 answers

Similar to what Flaviu said, you can put an implicit definition in an area somewhere:

  implicit def conditionalLoop[T](s: Seq[T]) = { new { def doWhile(p: T => Boolean): Unit = { doWhile(s.elements)(p) } private def doWhile(it: Iterator[T])(p: T => Boolean): Unit = { if (it.hasNext && p(it.next)) doWhile(it)(p) } } } 

Then the call is convenient:

 tasks doWhile { t => workflow.isAutoRun && t.run } 
+4
source

In scala 2.8 you can do things like this:

 breakable{ tasks.foreach{ task => if(!(workflow.isAutoRun&&runTask(task))) break } } 
+4
source

"exists" returns true and ends the loop after the predicate returns true:

 val completed = tasks.exists { task => if (workflow.isAutoRun) { runTask(task) false } else true } 

Edit:

Try the following:

 implicit def enrichList[A](list: List[A]) = new { def doWhile(cond: (A) => Boolean)(body: (A) => Unit) = list exists { x => if (cond(x)) { body(x) false } else true } } List(1, 2, 3, 4).doWhile(_ <= 3)(println) // prints "1 2 3" tasks.doWhile(_ => workflow.isAutoRun)(runTask) 
+2
source

How about an Iterable takeWhile :

 def takeWhile(p : (A) => Boolean): Iterable[A] 

Returns the longest prefix of this with iterable elements satisfying the predicate p

param p is a predicate test.

return is the longest prefix of this iterable whose elements satisfy the predicate p.

+2
source

Will this work?

 def doWhile[T](iter: Iterator[T])(predicate: T => Boolean): Unit = if(iter.hasNext && predicate(iter.next())) doWhile(iter)(predicate) 

It has a tail recursive so that it does not blow the stack. Since you need to run a predicate for each iterator element, the predicate is a function from T to Boolean .

Then your code will decrease to:

 doWhile(it) { task => workflow.isAutoRun && runTask(task) } 

Due to the nature of the side effects of your actions, the action is already performed when you evaluate the predicate (not very nice, but if it works with outdated code, you need to get around it :).

A more functional way to do this would be as follows:

 def doWhile[T](iter: Iterator[T])(predicate: => Boolean)(action: T => Unit): Unit = if(iter.hasNext && predicate) { action(iter.next) doWhile(iter)(predicate)(action) } 

Note. I could find a better name for this method than doWhile , but it was late ...

Hope this helps :)

- Flavy Chipchigan

+1
source

All Articles