Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: For loop that matches ints in a List

Tags:

scala

New to Scala. I'm iterating a for loop 100 times. 10 times I want condition 'a' to be met and 90 times condition 'b'. However I want the 10 a's to occur at random.

The best way I can think is to create a val of 10 random integers, then loop through 1 to 100 ints.

For example:

val z = List.fill(10)(100).map(scala.util.Random.nextInt)

z: List[Int] = List(71, 5, 2, 9, 26, 96, 69, 26, 92, 4)

Then something like:

for (i <- 1 to 100) {
  whenever i == to a number in z: 'Condition a met: do something'
else {
   'condition b met: do something else'
  }
}

I tried using contains and == and =! but nothing seemed to work. How else can I do this?

like image 239
RDJ Avatar asked Feb 19 '17 17:02

RDJ


2 Answers

Your generation of random numbers could yield duplicates... is that OK? Here's how you can easily generate 10 unique numbers 1-100 (by generating a randomly shuffled sequence of 1-100 and taking first ten):

val r = scala.util.Random.shuffle(1 to 100).toList.take(10)

Now you can simply partition a range 1-100 into those who are contained in your randomly generated list and those who are not:

val (listOfA, listOfB) = (1 to 100).partition(r.contains(_))

Now do whatever you want with those two lists, e.g.:

println(listOfA.mkString(","))
println(listOfB.mkString(","))

Of course, you can always simply go through the list one by one:

(1 to 100).map {
  case i if (r.contains(i)) => println("yes: " + i) // or whatever
  case i => println("no: " + i)
}

What you consider to be a simple for-loop actually isn't one. It's a for-comprehension and it's a syntax sugar that de-sugares into chained calls of maps, flatMaps and filters. Yes, it can be used in the same way as you would use the classical for-loop, but this is only because List is in fact a monad. Without going into too much details, if you want to do things the idiomatic Scala way (the "functional" way), you should avoid trying to write classical iterative for loops and prefer getting a collection of your data and then mapping over its elements to perform whatever it is that you need. Note that collections have a really rich library behind them which allows you to invoke cool methods such as partition.

EDIT (for completeness):

Also, you should avoid side-effects, or at least push them as far down the road as possible. I'm talking about the second example from my answer. Let's say you really need to log that stuff (you would be using a logger, but println is good enough for this example). Doing it like this is bad. Btw note that you could use foreach instead of map in that case, because you're not collecting results, just performing the side effects.

Good way would be to compute the needed stuff by modifying each element into an appropriate string. So, calculate the needed strings and accumulate them into results:

val results = (1 to 100).map {
  case i if (r.contains(i)) => ("yes: " + i) // or whatever
  case i => ("no: " + i)
}

// do whatever with results, e.g. print them

Now results contains a list of a hundred "yes x" and "no x" strings, but you didn't do the ugly thing and perform logging as a side effect in the mapping process. Instead, you mapped each element of the collection into a corresponding string (note that original collection remains intact, so if (1 to 100) was stored in some value, it's still there; mapping creates a new collection) and now you can do whatever you want with it, e.g. pass it on to the logger. Yes, at some point you need to do "the ugly side effect thing" and log the stuff, but at least you will have a special part of code for doing that and you will not be mixing it into your mapping logic which checks if number is contained in the random sequence.

like image 123
slouc Avatar answered Nov 11 '22 00:11

slouc


(1 to 100).foreach { x =>
  if(z.contains(x)) {
    // do something
  } else {
    // do something else
  }
}

or you can use a partial function, like so:

(1 to 100).foreach {
  case x if(z.contains(x)) => // do something
  case _ => // do something else
}
like image 33
Eric Avatar answered Nov 10 '22 22:11

Eric