Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting an error on this simple Scala code?

Tags:

scala

Here's my code:

object Triple {
  object Num extends Enumeration {
    type Num = Value
    val one, two, three, four, five, six, seven, eight, nine = Value
  }
  val triples = for {
    first <- Num.values
    second <- Num.values
    third <- Num.values
    if (first != second)
    if (first != third)
    if (second != third)
  } yield Set(first, second, third)
}

I get an error on the line with third. It says:

Multiple markers at this line
    - not enough arguments for method map: (implicit bf: 
         scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Se    t[sudoku.Triple.Num.Value],That])That. Unspecified 
 value parameter bf.
- diverging implicit expansion for type 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That] starting with 
 method newCanBuildFrom in object SortedSet
- not enough arguments for method map: (implicit bf: 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That])That. Unspecified 
 value parameter bf.
- diverging implicit expansion for type 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That] starting with 
 method newCanBuildFrom in object SortedSet

Update: Aleksey Izmailov gave a great answer. A slightly better implementation of the function is val triples=Num.values.subset(3).toSet

like image 201
Joe Avatar asked Oct 01 '22 13:10

Joe


1 Answers

Looks like what you are trying to do is this:

scala> object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
defined module Num

scala>   val triples = Num.values.toList.combinations(3)
triples: Iterator[List[Num.Value]] = non-empty iterator

scala> triples.toList
res0: List[List[Num.Value]] = List(List(one, two, three), List(one, two, four), List(one, two, five), List(one, two, six), List(one, two, seven), List(one, two, eight), List(one, two, nine), List(one, three, four), List(one, three, five), List(one, three, six), List(one, three, seven), List(one, three, eight), List(one, three, nine), List(one, four, five), List(one, four, six), List(one, four, seven), List(one, four, eight), List(one, four, nine), List(one, five, six), List(one, five, seven), List(one, five, eight), List(one, five, nine), List(one, six, seven), List(one, six, eight), List(one, six, nine), List(one, seven, eight), List(one, seven, nine), List(one, eight, nine), List(two, three, four), List(two, three, five), List(two, three, six), List(two, three, seven), List(two, three...
scala> 

If you do something like this then it works:

scala> object Triple {
     |   object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
     |   val triples = for {
     |     first <- Num.values
     |     second <- Num.values
     |     third <- Num.values
     |     if (first != second)
     |     if (first != third)
     |     if (second != third)
     |   } yield (first, second, third)
     | }
defined module Triple

scala> Triple.triples
res1: scala.collection.immutable.SortedSet[(Triple.Num.Value, Triple.Num.Value, Triple.Num.Value)] = TreeSet((one,two,three), (one,two,four), (one,two,five), (one,two,six), (one,two,seven), (one,two,eight), (one,two,nine), (one,three,two), (one,three,four), (one,three,five), (one,three,six), (one,three,seven), (one,three,eight), (one,three,nine), (one,four,two), (one,four,three), (one,four,five), (one,four,six), (one,four,seven), (one,four,eight), (one,four,nine), (one,five,two), (one,five,three), (one,five,four), (one,five,six), (one,five,seven), (one,five,eight), (one,five,nine), (one,six,two), (one,six,three), (one,six,four), (one,six,five), (one,six,seven), (one,six,eight), (one,six,nine), (one,seven,two), (one,seven,three), (one,seven,four), (one,seven,five), (one,seven,six), (one,...
scala> 

Note the result of the produced collection is a SortedSet. That result is defined by Num.values - you can convert for comprehension to map, flatMap and filter to check how the types come about.

The problem is that you can put stuff that can be sorted into SortedSet like this for example:

scala> scala.collection.immutable.SortedSet(1, 2)
res6: scala.collection.immutable.SortedSet[Int] = TreeSet(1, 2)

However, you can't put something that does not have ordering defined into a SortedSet because SortedSet guarantees that the stuff is sorted and should know how sorting is done (via implicit ordering):

scala> scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))
<console>:8: error: No implicit Ordering defined for scala.collection.immutable.Set[Int].
              scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))

Scala looks for implicit Ordering to be defined in scope and does not find one for the Set that you are using, or for the Set[Int] in my example.

Now let's "fix" it:

scala> implicit val dummyOrdering: Ordering[Set[Int]] = Ordering.by(_.size)
dummyOrdering: Ordering[Set[Int]] = scala.math.Ordering$$anon$9@6f00d9a7

scala> scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))
res8: scala.collection.immutable.SortedSet[scala.collection.immutable.Set[Int]] = TreeSet(Set(3, 4))

Obviously, this is a bad fix and the real problem is that in your case types are not well chosen. First of all you don't need to use a Set for each combination since you already comparing things to avoid repetition. Just use a Tuple3. Second, if you insist on using a Set, then convert Num.values to List in the for comprehension:

scala> object Triple {
     |   object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
     |   val triples = for {
     |     first <- Num.values.toList
     |     second <- Num.values.toList
     |     third <- Num.values.toList
     |     if (first != second)
     |     if (first != third)
     |     if (second != third)
     |   } yield Set(first, second, third)
     | }
defined module Triple

Hope that makes it clear. Basically the problem is in the choice of wrong types.

like image 199
yǝsʞǝla Avatar answered Oct 08 '22 06:10

yǝsʞǝla