1

Explanation: I accepted gzm0's answer because it rocked!

@Eduardo did come in with a comment suggesting:

(for(i <- 10..20; j=runTest(i)) yield i -> j).toMap

which also lets me run build, he just never posted an answer and @gzm0 answer was conceptually AWESOME so I accepted it.

Once I get this other issue figured out relating to "can't call constructor" I will be able to test these out by actually running the program LOL

Question: I have an error in this expression, specifically how to fix it but more generally what am I missing about FP or Scala to make this mistake?

timingsMap = for (i <- powersList; j <- runTest(i)) yield i -> j

I am Writing my first Gradle/Scala project for a Analysis of Algorithms assignment. Scala is not part of the assignment so I am not using a homework tag. Except for my work with Spark in Java, I am brand new to Functional Programming I am sure that's the problem.

Here's a snippet, the full .scala file is on GitHub, is that OK or I will post the full program here if I get flailed :)

val powersList = List(10 to 20)

// create a map to keep track of power of two and resulting timings
var timingsMap: Map[Integer, Double] = Map()

// call the runTest function once for each power of two we want, from powersList,
// assign timingsMap the power of 2 value and results of runTest for that tree size
timingsMap = for (i <- powersList; j <- runTest(i)) yield i -> j

The error is: /home/jim/workspace/Scala/RedBlackTree4150/src/main/scala/Main.scala:36: value map is not a member of Double timingsMap = for (i <- powersList; j <- runTest(i)) yield i -> j

What I think I am doing in that timingsMap = ... line is get all the elements of powersList mapped onto i for each iteration of the loop, and the return value for runTest(i) mapped onto j for each iteration, and then taking all those pairs and putting them into timingsMap. Is it the way I am trying to use i in the loop to call runTest(i) that causes the problem?

runTest looks like this:

def runTest(powerOfTwo: Range): Double = {

    // create the tree here
    var tree = new TreeMap[Int, Double]

    // we only care to create a tree with integer keys, not what the value is
    for (x <- powerOfTwo) {
      // set next entry in map to key, random number
      tree += (x -> math.random)
    }

    stopWatchInst.start()

    // now go through and look up all the values in the tree,
    for (x <- powerOfTwo) {
      // get the value, don't take the time/overhead to store it in a var, as we don't need it
      tree.get(x)
    }

    // stop watch check time report and return time
    stopWatchInst.stop()
    val totalTime = stopWatchInst.elapsed(TimeUnit.MILLISECONDS)
    loggerInst.info("run for 2 to the power of " + powerOfTwo + " took " + totalTime + " ms")
    return totalTime
  }

Note: I've had a suggestions to change the j <- to = in j <- in this line: timingsMap = for (i <- powersList; j <- runTest(i)) yield i -> j

Another suggestion didn't like using yield at all and suggested replacing with (10 to 20).map...

The strange part is the existing code does not show an error in the IntelliJ editor, only breaks when I run it. The suggestions all give type mismatch errors in the IDE. I am really trying to figure out conceptually what I am doing wrong, thanks for any help! (and of course I need to get it to work!)


After trying gzm0 answer I am getting down the same road ... my code as presented doesn't show any type mismatches until I use gradle run ... whereas when I make the suggested changes it starts to give me errors right in the IDE ... but keep em coming! Here's the latest error based on gzm0s answer:

/home/jim/workspace/Scala/RedBlackTree4150/src/main/scala/Main.scala:37: type mismatch;
 found   : List[(scala.collection.immutable.Range.Inclusive, Double)]
 required: Map[Integer,Double]
  timingsMap = for (i <- powersList) yield i -> runTest(i)
Aaron_ab
  • 3,450
  • 3
  • 28
  • 42
JimLohse
  • 1,209
  • 4
  • 19
  • 44
  • 1
    Took the parens off class `RedBlackTree4150()` made no difference I can see ... I have access through Safari Books to Learning Scala and Wampler's Programming Scala so if there's a good reference in there please use chapter/section titles not page numbers thanks :) – JimLohse Jan 25 '16 at 02:18
  • There was an answer posted but it was turning into a long discussion and he didn't want to go to chat, I wish that user had not deleted the answer! They were suggesting I use something like (10 to 20).map but we got into a lot of type mismatches. I think what I am doing with `for ... yield` is the same as map, under the hood. Is there a reason to avoid `for ... yield`? That user didn't like it. – JimLohse Jan 25 '16 at 03:36
  • 1
    use `j = runTest(i)` with an `=` and not `<-`. The `<-` sign is for when you have collections. `runTest` just returns a `Double` value – Eduardo Jan 25 '16 at 03:40
  • Very helpful, got me to look at the Scala spec to learn more, but I still get a type mismatch `/home/jim/workspace/Scala/RedBlackTree4150/src/main/scala/Main.scala:36: type mismatch; found : List[(scala.collection.immutable.Range.Inclusive, Double)] required: Map[Integer,Double] timingsMap = for (i <- powersList; j = runTest(i)) yield i -> j` – JimLohse Jan 25 '16 at 03:45
  • The timingsMap is supposed to be a group of tuples with (power of 2 tested, an integer; time it took to run, a double) ... I will add a note to the end of my question – JimLohse Jan 25 '16 at 03:46
  • Use `val powersList = 10 to 20`. `10 to 20` gives you a range, and the `<-` will extract from that, integer by integer. There is no need to wrap it inside a List. – Eduardo Jan 25 '16 at 03:51
  • I forget now why I made it a list ... now runTest doesn't have the right signature to make this work ... rather than getting into a long discussion here maybe post an answer and we can comment there? ... I see gzm0 just posted an answer let me check that out – JimLohse Jan 25 '16 at 03:55
  • 1
    OK. I think, once you reach that point, you can fix it by wrapping the whole for/yield expression in parenthesis and calling `.toMap` – Eduardo Jan 25 '16 at 03:58

1 Answers1

3

You want:

for (i <- powersList)
  yield i -> runTest(i)

The result of runTest is not a list, therefore you can't give it to the for statement. The reason that you get a bit of a strange error message, is due to how your for is desugared:

for (i <- powersList; j <- runTest(i)) yield i -> j

// turns into

powersList.flatMap { i => runTest(i).map { j => i -> j } }

However, the result of runTest(i) is a Double, which doesn't have a map method. So looking at the desugaring, the error message actually makes sense.

Note that my point about runTest's result not being a list is not really correct: Anything that has a map method that will allow the above statement (i.e. taking some kind of lambda) will do. However, this is beyond the scope of this answer.

We now have successfully created a list of tuples (since powersList is a List, the result of the for loop is a List as well). However, we want a Map. Luckily, you can call toMap on Lists that contain tuples to convert them into a Map:

val tups = for (i <- powersList) yield i -> runTest(i)
timingsMap = tups.toMap

A note aside: If you really want to keep the j inside the for-loop, you can use the equals sign instead of the left arrow:

for (i <- powersList; j = runTest(i)) yield i -> j

This will turn into:

powersList.map { i =>
  val j = runTest(i)
  i -> j
}

Which is what you want.

gzm0
  • 14,752
  • 1
  • 36
  • 64
  • I put `timingsMap = for (i <- powersList) yield i -> runTest(i)` and get this error: `/home/jim/workspace/Scala/RedBlackTree4150/src/main/scala/Main.scala:37: type mismatch; found : List[(scala.collection.immutable.Range.Inclusive, Double)] required: Map[Integer,Double] timingsMap = for (i <- powersList) yield i -> runTest(i)` which I will edit into my question for readability – JimLohse Jan 25 '16 at 03:59
  • @ChrisMartin I know. Added more explanation. It still is not clear enough, I'll improve. – gzm0 Jan 25 '16 at 03:59
  • Let me read your revised answer, I added the error to the end of my answer ... really appreciate everyone's suggestions but they start me down a path of type mismatches ... which is OK if it lead somewhere :) .... AHHHH just read your extended answer VERY COOL THANKS let me try that, that's the explanation I have been waiting for! – JimLohse Jan 25 '16 at 04:02
  • Isn't there still an error? In my answer I assign a `Map[Range, Double]` to a `Map[Integer, Double]`. Do you want the map to contain range -> time or point -> time mappings? – gzm0 Jan 25 '16 at 04:06
  • ding ding ding!!! (the good ding) we have a winner. Very complete answer, that totally explains why I was not seeing an error in the IDE, only on run. Thanks for the super-explanation, it now runs and only has the two errors in another question I asked. You da man! The other question BTW is http://stackoverflow.com/questions/34983987/scala-constructor-stopwatch-cannot-be-accessed-in-class-main which I think is really the easy one so I am surprise no one has jumped on it yet thanks again!!!! – JimLohse Jan 25 '16 at 04:07
  • on your last comment, I think you have given me the tools I need, I want the timingsMap to have tuples of (power of 2 tests, time it took to run). Once I get the other linked question sorted out I can start actually running this thing – JimLohse Jan 25 '16 at 04:08