0

I'm trying to create a data structure that has a PriorityQueue in it. I've succeeded in making a non-generic version of it. I can tell it works because it solves the A.I. problem I have.
Here is a snippet of it:

class ProntoPriorityQueue { //TODO make generic

implicit def orderedNode(node: Node): Ordered[Node] = new Ordered[Node] {
   def compare(other: Node) = node.compare(other)
}

val hashSet = new HashSet[Node]
val priorityQueue = new PriorityQueue[Node]()
...

I'm trying to make it generic, but if I use this version it stops solving the problem:

class PQ[T <% Ordered[T]] {
//[T]()(implicit val ord: T => Ordered[T]) {
//[T]()(implicit val ord: Ordering[T] {

val hashSet = new HashSet[T]
val priorityQueue = new PriorityQueue[T]
...

I've also tried what's commented out instead of using [T <% Ordered[T]]

Here is the code that calls PQ:

//the following def is commented out while using ProntoPriorityQueue
implicit def orderedNode(node: Node): Ordered[Node] = new Ordered[Node] {
     def compare(other: Node) = node.compare(other)
} //I've also tried making this return an Ordering[Node]

val frontier = new PQ[Node] //new ProntoPriorityQueue
//have also tried (not together): 
val frontier = new PQ[Node]()(orderedNode)

I've also tried moving the implicit def into the Node object (and importing it), but essentially the same problem.

What am I doing wrong in the generic version? Where should I put the implicit?


Solution The problem was not with my implicit definition. The problem was the implicit ordering was being picked up by a Set that was automatically generating in a for(...) yield(...) statement. This caused a problem where the yielded set only contained one state.

Tombstone
  • 157
  • 1
  • 13

2 Answers2

1

What's wrong with simply defining an Ordering on your Node (Ordering[Node]) and using the already-generic Scala PriorityQueue?

As general rule, it's better to work with Ordering[T] than T <: Ordered[T] or T <% Ordered[T]. Conceptually, Ordered[T] is an intrinsic (inherited or implemented) property of the type itself. Notably, a type can have only one intrinsic ordering relationship defined this way. Ordering[T] is an external specification of the ordering relationship. There can any be any number of different Ordering[T].

Also, if you're not already aware, you should know that the difference between T <: U and T <% U is that while the former includes only nominal subtype relations (actual inheritance), the latter also includes the application of implicit conversions that yield a value conforming to the type bound.

So if you want to use Node <% Ordered[Node] and you don't have a compare method defined in the class, an implicit conversion will be applied every time a comparison needs to be made. Additionally, if your type has its own compare, the implicit conversion will never be applied and you'll be stuck with that "built-in" ordering.

Addendum

I'll give a few examples based on a class, call it CIString that simply encapsulates a String and implements ordering as case-invariant.

/* Here's how it would be with direct implementation of `Ordered` */

class   CIString1(val s: String)
extends Ordered[CIString1]
{
  private val lowerS = s.toLowerCase

  def compare(other: CIString1) = lowerS.compareTo(other.lowerS)
}

/* An uninteresting, empty ordered set of CIString1
    (fails without the `extends` clause) */
val os1 = TreeSet[CIString1]()


/* Here's how it would look with ordering external to `CIString2`
    using an implicit conversion to `Ordered` */

class CIString2(val s: String) {
  val lowerS = s.toLowerCase
}

class CIString2O(ciS: CIString2)
extends Ordered[CIString2]
{
  def compare(other: CIString2) = ciS.lowerS.compareTo(other.lowerS)
}

implicit def cis2ciso(ciS: CIString2) = new CIString2O(ciS)

/* An uninteresting, empty ordered set of CIString2
    (fails without the implicit conversion) */
val os2 = TreeSet[CIString2]()


/* Here's how it would look with ordering external to `CIString3`
    using an `Ordering` */

class CIString3(val s: String) {
  val lowerS = s.toLowerCase
}

/* The implicit object could be replaced by
    a class and an implicit val of that class */
implicit
object  CIString3Ordering
extends Ordering[CIString3]
{
  def compare(a: CIString3, b: CIString3): Int = a.lowerS.compareTo(b.lowerS)
}

/* An uninteresting, empty ordered set of CIString3
    (fails without the implicit object) */
val os3 = TreeSet[CIString3]()
Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
  • I've tried extending `Ordering[Node]` on my Node. I've implemented both a `compare(other: Node)` and `compare(some: Node, other: Node)` This caused a bunch of problems in other method calls that involve `Node` Here are some of them: - diverging implicit expansion for type scala.collection.generic.CanBuildFrom[main.Moves.ValueSet,Option[main.Node],That] starting with method newCanBuildFrom in object SortedSet - not enough arguments for method map: (implicit bf: scala.collection.generic.CanBuildFrom[main.Moves.ValueSet,Option[main.Node],That])That. Unspecified value parameter bf – Tombstone Feb 13 '13 at 20:38
  • @Tombstone: You don't make your `Node` class extend `Ordering[Node]`, you define a class that extends `Ordering[Node]` that defines the ordering relationship on `Node` by defining the method `compare`. – Randall Schulz Feb 13 '13 at 21:49
  • I've made a new class that extends Ordering[Node] and it works. Thank you. I still have a question regarding implicits. How come my previous implicits didn't work? `implicit def nodeOrderer: Ordering[Node] = new Ordering[Node]{ def compare(some: Node, other: Node) = some.compare(other) }` `implicit val ordN: Ordering[Node] = new Ordering[Node] { def compare(some: Node, other: Node) = some.compare(other) }` – Tombstone Feb 14 '13 at 01:46
  • @Tombstone: You have to combine properly typed (they were) in-scope (they would have been, were they not commented out) implicit conversions _with_ the `<%` type bound, not instead of it. – Randall Schulz Feb 14 '13 at 01:49
  • Thanks for your patients helping me. Sadly I still haven't figured it out. Could you please give me how you would define the class, a call to the constructor, and the implicit def to use? – Tombstone Feb 14 '13 at 02:50
  • @Tombstone: Did the examples help? – Randall Schulz Feb 15 '13 at 02:30
  • Yes the examples help. Thank you. I've come to the conclusion through testing and debugging that whenever I define an `Ordering` or `Ordered` for Node implicitly, it screws up the algorithm. After some investigation, I found that it likely had to do with a `for(...) yield (...)` call, because it was returning a `SortedSet[Node]` with just one item. After making the loop use a `MutableList`, the implicit works -but produces a higher cost solution. That's another mystery I'll have to figure out. – Tombstone Feb 20 '13 at 05:20
0

Well, one possible problem is that your Ordered[Node] is not a Node:

implicit def orderedNode(node: Node): Ordered[Node] = new Ordered[Node] {
  def compare(other: Node) = node.compare(other)
}

I'd try with an Ordering[Node] instead, which you say you tried but there isn't much more information about. PQ would be declared as PQ[T : Ordering].

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • I've changed `PQ` definition to: `class PQ[T : Ordering] {...` and changed my implicit def to: `implicit def orderedNode: Ordering[Node] = new Ordering[Node] { def compare(some: Node, other: Node) = some.compare(other) }` and it still doesn't work – Tombstone Feb 13 '13 at 20:10
  • @Tombstone Well, there's not enough information. All you are giving us is that an algorithm that depends on it isn't working any longer -- which I suspect to be a bug in the algorithm. In the absence of a "it does X when I do Y, and it should do Z instead", all we can do is guesswork. – Daniel C. Sobral Feb 14 '13 at 01:04
  • There could be problem in the algorithm, but I doubt there is for a few reasons: 1.) It's based on my graph search algorithm that has worked for other data structures for the explored set (LIFO, FIFO) and even w/o an explored set 2.) The biggest difference is changing from a stack||queue to a priority queue 3.) I had it working using a data structure that was non-generic (and now have a non-generic, non-implicit version working). To put all relevant information of the algorithm would take hundreds of lines. https://www.dropbox.com/s/lw63j1vq8k94mu4/tombstone%20src.zip – Tombstone Feb 14 '13 at 02:33