1

I was given a challenge recently in school to create a simple program in Scala the does some calculations in a matrix, the thing is I have to do these calculations using 5 threads, since I had no prior knowledge of Scala I am stuck. I searched online but I did not find how to create the exact number of threads I want. This is the code:

import scala.math

object Test{

  def main(args: Array[String]){

    val M1: Seq[Seq[Int]] = List(
      List(1, 2, 3),
      List(4, 5, 6),
      List(7, 8, 9)
    )

    var tempData : Float= 0
    var count:Int = 1
    var finalData:Int=0

    for(i<-0 to M1.length-1; j<-0 to M1(0).length-1){

      count = 1

      tempData = M1(i)(j)+ calc(i-1,j)+calc(i,j-1)+calc(i+1,j)
      finalData = math.ceil(tempData/count).toInt
      printf("%d ", finalData)
    }

    def calc(i:Int, j:Int): Int ={

      if((i<0)|| (j<0) || (i>M1.length-1))
        return 0

      else{
        count +=1
        return M1(i)(j)}
      }
    }

I tried this:

    for (a <- 0 until 1) {
      val thread = new Thread {
        override def run { 

          for(i<-0 to M1.length-1; j<-0 to M1(0).length-1){

            count = 1

            tempData = M1(i)(j)+ calc(i-1,j)+calc(i,j-1)+calc(i+1,j)
            finalData = math.ceil(tempData/count).toInt
            printf("%d ", finalData)
          }
        }
      }
      thread.start
    }

but it only executed the same thing 10 times

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • side note : you seem to be adding unnecessary type annotations which hurts readability e.g. `var count:Int = 1` => `var count = 1` – niceman Jun 17 '17 at 23:42
  • ok thanks, this is actually my first day learning scala so I dont know all the best practices yet – Doctor Zero Jun 17 '17 at 23:42
  • `a <- 0 until 1` until is exclusive in its upper bound so the for loop will only execute one time creating only one thread, try to replace `until` with `to` for example and see for yourself – niceman Jun 17 '17 at 23:47
  • yes I tried that and it worked, actually the challenge is to create a single thread version and a multi thread version @niceman – Doctor Zero Jun 17 '17 at 23:49
  • "ince I had no prior knowledge of Scala I am stuck." I think the idea of being at school is to study, learn, then be able to do new things. I really don't understand why anyone would take a course, then look for other people to do the assignments for them. – The Archetypal Paul Jun 18 '17 at 11:34
  • Like I said this is a challenge, not a course @TheArchetypalPaul. That means I have no lessons in Scala – Doctor Zero Jun 18 '17 at 11:35
  • "but it only executed the same thing 10 times", well, yes, since that's exactly what you defined it to do. What are you looking to do? If it's parallelise the calculations, you can't just put the whole code in a thread, you need to put individual parts of the calculation in their own thread – The Archetypal Paul Jun 18 '17 at 11:37
  • Ok. but "challenge" is pretty much a synonym for exercise or assignment, really. Same thing applies - how does it benefit you to get someone else to meet the challenge? – The Archetypal Paul Jun 18 '17 at 11:38
  • Im not asking for you to do the assignment for me, just to give me some "directions" – Doctor Zero Jun 18 '17 at 11:40
  • OK. Answer coming. BTW, since you only have 9 cells, it's not really possible to use 10 threads. Will the real calculation have a larger matrix? – The Archetypal Paul Jun 18 '17 at 11:41
  • Yes, the program will perform the calculations on that matrix and in a 20x20 matrix, this is the actual challenge if you need some details https://github.com/premium-minds/summer-internship-exercise – Doctor Zero Jun 18 '17 at 11:43
  • "Answer coming. " Will be delayed. Real life intrudes. Hopefully someone else will get there first. – The Archetypal Paul Jun 18 '17 at 11:50
  • thanks for the help @TheArchetypalPaul – Doctor Zero Jun 18 '17 at 12:04
  • Looks like you programming Java in Scala. I am very disappointed see that from a Portuguese institution. :( – pedrofurla Jun 18 '17 at 18:49
  • I simply never used scala before, this is a challenge presented by a company in my school to get a summer internship but they gave me the challenge during my exams week so I basically had to learn scala in one day – Doctor Zero Jun 18 '17 at 18:52

2 Answers2

1

Here's the original core of the calculation.

for(i<-0 to M1.length-1; j<-0 to M1(0).length-1){

  count = 1

  tempData = M1(i)(j)+ calc(i-1,j)+calc(i,j-1)+calc(i+1,j)
  finalData = math.ceil(tempData/count).toInt
  printf("%d ", finalData)
}

Let's actually build a result array

val R = Array.ofDim[Int](M1.length, M1(0).length)

var tempData : Float= 0
var count:Int = 1
var finalData:Int=0

for(i<-0 to M1.length-1; j<-0 to M1(0).length-1){

  count = 1

  tempData = M1(i)(j)+ calc(i-1,j)+calc(i,j-1)+calc(i+1,j)
  R(i)(j) = math.ceil(tempData/count).toInt
}

Now, that mutable count modified in one function and referenced in another is a bit of a code smell. Let's remove it - change calc to return an option, assemble a list of the things to average, and flatten to keep only the Some

val R = Array.ofDim[Int](M1.length, M1(0).length)

for (i <- 0 to M1.length - 1; j <- 0 to M1(0).length - 1) {

  val tempList = List(Some(M1(i)(j)), calc(i - 1, j), calc(i, j - 1), calc(i + 1, j)).flatten
  R(i)(j) = math.ceil(tempList.sum.toDouble / tempList.length).toInt
}

def calc(i: Int, j: Int): Option[Int] = {

  if ((i < 0) || (j < 0) || (i > M1.length - 1))
    None

  else {

    Some(M1(i)(j))
  }
}

Next, a side-effecting for is a bit of a code smell too. So in the inner loop, let's produce each row and in the outer loop a list of the rows...

val R = for (i <- 0 to M1.length - 1) yield {
  for (j <- 0 to M1(0).length - 1) yield {

    val tempList = List(Some(M1(i)(j)), calc(i - 1, j), calc(i, j - 1), calc(i + 1, j)).flatten
    math.ceil(tempList.sum / tempList.length).toInt
  }
}

Now, we read the Scala API and we notice ParSeq and Seq.par so we'd like to work with map and friends. So let's un-sugar the for comprehensions

val R = (0 until M1.length).map { i =>
  (0 until M1(0).length).map { j =>

    val tempList = List(Some(M1(i)(j)), calc(i - 1, j), calc(i, j - 1), calc(i + 1, j)).flatten
    math.ceil(tempList.sum / tempList.length).toInt
  }
}

This is our MotionBlurSingleThread. To make it parallel, we simply do

val R = (0 until M1.length).par.map { i =>
  (0 until M1(0).length).par.map { j =>

    val tempList = List(Some(M1(i)(j)), calc(i - 1, j), calc(i, j - 1), calc(i + 1, j)).flatten
    math.ceil(tempList.sum / tempList.length).toInt
  }.seq
}.seq

And this is our MotionBlurMultiThread. And it is nicely functional too (no mutable values)

The limit to 5 or 10 threads isn't in the challenge on Github, but if you need to do that you can look at scala parallel collections degree of parallelism and related questions

The Archetypal Paul
  • 41,321
  • 20
  • 104
  • 134
  • The code works just fine except it is giving me the wrong result. it should give me (3,3,4)(4,5,6)(6,7,8) but instead it is giving me (2, 2, 3)(4, 4, 5)(5, 6, 7), this was happening before, I printed the result inside the for loop and it was fine but when I printed the result outside the for loop it gave me the wrong result – Doctor Zero Jun 18 '17 at 17:25
  • this is the final project, it runs but I cant find the problem I stated above: https://github.com/RodrigoPina407/summer-intership-exercise – Doctor Zero Jun 18 '17 at 17:45
  • I just copied your calculation using i and j, but I think you may have some confusion over which is x and which is y. I will leave you to work the correction out :) – The Archetypal Paul Jun 18 '17 at 19:55
  • I thought about that but before, the calculation inside the for loop was correct but in the return statement was wrong – Doctor Zero Jun 18 '17 at 19:57
  • but I will double check, thank you for your help, this was a hard task because the challenge was given during my exams week so I had onoy yesterday and today to learn scala from scratch, thanks again – Doctor Zero Jun 18 '17 at 20:06
  • I reversed the i and j calculations and I'm still getting errors, but when I trace the calculations by hand it gives me the right results so I dont understand what is happening – Doctor Zero Jun 18 '17 at 20:39
  • Add lots of printlns! If it works by hand, then I really do think you're getting confused about i, j and x,y. In your head, you're doing the right thing so getting the right answer. In the code, it might be wrong. – The Archetypal Paul Jun 18 '17 at 20:46
  • Ah `resultado(x, y) = (M1(x, y)+M1(x-1, y)+M1(x, y-1)+M1(x, y+1)) / 4`. That last term is different from yours. It's `i, j+1`. You have `i+1, j` – The Archetypal Paul Jun 18 '17 at 20:52
  • Ok it almost fixed thanks, the only values wrong are the first and the last, thank you for the help – Doctor Zero Jun 18 '17 at 20:58
  • The readme says to average the pixel+pixel above+pixel below+pixel to the left, as long as they exist so it should be M1(i,j)+M1(i-1,j) +M1(i-1,j)+M1(i,j-1), right? – Doctor Zero Jun 18 '17 at 21:12
  • In `M1(i)(j)`, which do you think is `x` and which `y`? Once you've decided that, it's a direct translation of the `resultado` definition. – The Archetypal Paul Jun 18 '17 at 21:15
  • the i is the y and the j is the x, and the result is correct by doing it this way but it isn't what ir shows, because in my previous implementation the resulto was correct inside the for loop but incorrect in the return statement – Doctor Zero Jun 18 '17 at 21:17
  • And i tried to put println inside the for loops but it gives an error – Doctor Zero Jun 18 '17 at 21:20
  • Shouldn't do, as long as it's not the last statement. Remember the last statement must yield the value. So you may need to save the result in a `val` in order that you can print it, then return it as the last value. – The Archetypal Paul Jun 18 '17 at 21:22
  • But this is now "normal" debugging. I'm sure you can sort this on your own. And anyway, it's late here :) – The Archetypal Paul Jun 18 '17 at 21:24
  • ok I managed to put the println and the values are the same but the calculation are wrong, im trying to figure out why – Doctor Zero Jun 18 '17 at 21:28
-2

I am not an expert, neither on Scala nor on concurrency. Scala approach to concurrency is through the use of actors and messaging, you can read a little about that here, Programming in Scala, chapter 30 Actors and Concurrency (the first edition is free but it is outdated). As I was telling, the edition is outdated and in the latest version of Scala (2.12) the actors library is no longer included, and they recommend to use Akka, you can read about that here.

So, I would not recommend learning about Scala, Sbt and Akka just for a challenge, but you can download an Akka quickstart here and customize the example given to your needs, it is nicely explained in the link. Each instance of the Actor has his own thread. You can read about actors and threads here, in specific, the section about state.

  • sorry, I really try not to do that, but even if there are newest versions (at a cost) I think the free edition is a common learning resource for Scala developers and most of the concepts are still valid. In this case it was important to say that is outdated because the library is not there anymore, but the approach is still valid using the new Akka library. – Migsar Navarro Jun 18 '17 at 13:19
  • Actors are *one* approach to concurrency in Scala. Parallel collections are another, Futures are another. Stream (Fs2, scalaz.streams) yet another. Not to mention that everything written for Java concurrency is also available. Therefore you answer is wrong. – pedrofurla Jun 18 '17 at 18:37
  • I did not say EVER my solution was exhaustive. I started by clarifying that I was not an expert and I still believe it is the recommended approach. Sure you can use everything available in Java but then why not stick to Java, I think you should write an improved answer that enlighten us. – Migsar Navarro Jun 18 '17 at 19:08
  • "Scala approach to concurrency is through the use of actors and messaging, you can read a little about that here,..." – pedrofurla Jun 18 '17 at 20:27
  • I think that is valid, if you search for "Scala approach to concurrency" on a search engine you'll get Actors at the top of the results, maybe I should have told the most common approach, or one of many approaches, I did not say it was the only approach either. To say that the approach is something does not imply that any other method is banned, just that it is common practice, and if someone without familiarity with some topic is searching for a quick answer I think support in terms of information available is a good criteria. But I'll really try to be much more specific next time. – Migsar Navarro Jun 18 '17 at 21:38
  • "Scala's approach to concurrency is..." does say to me that you are saying that is the way to do it. It's also a pretty bad match for the OP's problem. If you're not an expert in Scala or concurrency, maybe it would be better to move on and answer another question. – The Archetypal Paul Jun 19 '17 at 07:08