snappy
snappy

Reputation: 2781

Initializing a val to be used in a different scope

How can I initialize a val that is to be used in another scope? In the example below, I am forced to make myOptimizedList as a var, since it is initialized in the if (iteration == 5){} scope and used in the if (iteration > 5){} scope.

val myList:A = List(...)
var myOptimizedList:A = null

for (iteration <- 1 to 100) {
    if (iteration < 5) {
        process(myList)
    } else if (iteration == 5)
        myOptimizedList = optimize(myList)
    } 
    if (iteration > 5) {
        process(myOptimizedList)
    }
}

This may have been asked before, but I wonder if there is an elegant solution that uses Option[A].

Upvotes: 2

Views: 1096

Answers (4)

Rex Kerr
Rex Kerr

Reputation: 167891

You can almost always rewrite some sort of looping construct as a (tail) recursive function:

@annotation.tailrec def processLists(xs: List[A], start: Int, stop: Int) {
  val next = start + 1
  if (start < 5) { process(xs); processLists(xs, next, stop)
  else if (start == 5) { processLists( optimize(xs), next, stop) }
  else if (start <= stop) { process(xs); processLists( xs, next, stop ) }
}
processLists(myList, 100, 1)

Here, you pass forward that data which you would otherwise have mutated. If you need to mutate a huge number of things it becomes unwieldy, but for one or two it is often as clear or clearer than doing the mutation.

Upvotes: 3

Don Mackenzie
Don Mackenzie

Reputation: 7963

There's another technique (perhaps trick in this case) to delay initialization of myOptimizedList which is to use a lazy val. Your example is very specific but the principal is still obvious, delay assignment of a val until it is first referenced.

val      myList          = List(A(), A(), A())
lazy val myOptimizedList = optimize(myList)

for (iteration <- 1 to 100) {
  if (iteration < 5)
    process(myList)
  else if (iteration > 5)
    process(myOptimizedList)
}

Note that the case iteration == 5 is ignored.

Upvotes: 2

tenshi
tenshi

Reputation: 26566

Seems that you have taken this code example out of the context, so this solution can be not very suitable for your real context, but you can use foldLeft in order to simplify it:

val myOptimizedList = (1 to 100).foldLeft (myList) {
    case (list, 5) => optimize(list)
    case (list, _) => process(list); list
}

Upvotes: 8

dhg
dhg

Reputation: 52681

It's often the case that you can rework your code to avoid the problem. Consider the simple, and common, example here:

var x = 0
if(something)
  x = 5
else
  x = 6
println(x)

This would be a pretty common pattern in most languages, but Scala has a better way of doing it. Specifically, if-statements can return values, so the better way is:

val x = 
  if(something)
    5
  else
    6
println(x)

So we can make x a val after all.

Now, clearly your code can be rewritten to use all vals:

val myList:A = List(...)
for (iteration <- 1 to 5)
  process(myList)
val myOptimizedList = optimize(myList)
for (iteration <- 5 to 100)
  process(myOptimizedList)

But I suspect this is simply an example, not your real case. But if you're unsure how you might rearrange your real code to accomplish something similar, please show us what it looks like.

Upvotes: 2

Related Questions