views:

213

answers:

3

I was trying to write a testing/timing function for answers provided in this SO question. Some answers work on Array[T], some on List[T], one on Iterable[T] and one on String!

What I'd like to write is a function that takes the shift* functions from the question or the answers, an input list, a predicate and an expected output and run the function. Sort of like:

def test[T](
  func:(Seq[T], T=>Boolean) => Seq[T],
  input:Seq[T],
  predicate:T=>Boolean,
  expected:Seq[T]): Unit = {
    // may be some warm up
    // ... time start, run func, time stop, 
    // check output against expected
}

Except I can figure out the signature, as Array seems to have mutable Seq properties, while List seems to have immutable Seq properties.

What's the best way to handle that?

Edit: Using Thomas' suggestion this is how close I can get (works on Array[Char], List[T] but not on Array[T]):

val inputArr = Array('a', 'b', 'C', 'D')
val expectArr = Array('a', 'C', 'D', 'b')
val inputList = inputArr.toList
val expectList = expectArr.toList

def test[I, T](
  func:(I, T=>Boolean) => Traversable[T],
  input: I,
  predicate: T=>Boolean,
  expected: Traversable[T]): Boolean = {
  val result = func(input, predicate)
  if (result.size == expected.size) {
    result.toIterable.zip(expected.toIterable).forall(x => x._1 == x._2)
  } else {
    false
  }
}

// this method is from Geoff [there][2] 
def shiftElements[A](l: List[A], pred: A => Boolean): List[A] = {
  def aux(lx: List[A], accum: List[A]): List[A] = {
    lx match {
      case Nil => accum
      case a::b::xs if pred(b) && !pred(a) => aux(a::xs, b::accum)
      case x::xs => aux(xs, x::accum)
    }
  }
  aux(l, Nil).reverse
}

def shiftWithFor[T](a: Array[T], p: T => Boolean):Array[T] = {
  for (i <- 0 until a.length - 1; if !p(a(i)) && p(a(i+1))) {
    val tmp = a(i); a(i) = a(i+1); a(i+1) = tmp
  }
  a
}

def shiftWithFor2(a: Array[Char], p: Char => Boolean):Array[Char] = {
  for (i <- 0 until a.length - 1; if !p(a(i)) && p(a(i+1))) {
    val tmp = a(i); a(i) = a(i+1); a(i+1) = tmp
  }
  a
}

def shiftMe_?(c:Char): Boolean = c.isUpper

println(test(shiftElements[Char], inputList, shiftMe_?, expectList))
println(test(shiftWithFor2, inputArr, shiftMe_?, expectArr))
//following line does not compile
println(test(shiftWithFor, inputArr, shiftMe_?, expectArr))
//found   : [T](Array[T], (T) => Boolean) => Array[T]
//required: (?, (?) => Boolean) => Traversable[?]

//following line does not compile
println(test(shiftWithFor[Char], inputArr, shiftMe_?, expectArr))
//found   : => (Array[Char], (Char) => Boolean) => Array[Char]
//required: (?, (?) => Boolean) => Traversable[?]

//following line does not compile
println(test[Array[Char], Char](shiftWithFor[Char], inputArr, shiftMe_?, expectArr))
//found   : => (Array[Char], (Char) => Boolean) => Array[Char]
//required: (Array[Char], (Char) => Boolean) => Traversable[Char]

I will mark Daniel's answer as accepted as it compiles and provides me a different way to achieve what I wanted - unless the method on Array[T] creates a new array (and brings in Manifest issues).

(2): http://stackoverflow.com/questions/2244885/how-would-be-a-functional-approach-to-shifting-certain-array-elements/2245438#2245438

+3  A: 

One way is to define the function:

def test[I, T](
  func:(I, T=>Boolean) => Traversable[T],
  input: I,
  predicate: T=>Boolean,
  expected: Traversable[T]): Unit = {
  println(func(input, predicate))
}

def g(x : Char) = true

test((x : String, y: Char => Boolean) => x, "asdf", g _ , "expected")
test((x : List[Char], y: Char => Boolean) => x, List('s'), g _, List('e'))
test((x : Array[Char], y: Char => Boolean) => x, Array('s'), g _, Array('e'))
test((x : Iterable[Char], y: Char => Boolean) => x, Set('s'), g _, Set('e'))
Thomas Jung
I really meant for `test` to call `func(input, predicate)`. That causes a `found: input.type, required: I`. I can fix that with changing to `input:I` in the `test` declaration. Then I get `inferred type arguments [Array[Char],Char] do not conform to method test's type parameter bounds [I <: Traversable[T],T]` for the `Array[Char]` example. The `String` example also has issue. The `List` and `Iterable` work.
huynhjl
You can drop the I <: Traversable[T] constraint. You could do it as well for the return types (func:(I, T=>Boolean) => I and expected: I).
Thomas Jung
+1  A: 

All the types you mention (even String) are eithr explicitly (List) or implicitly (Array and String) Iterable, so all you have to do is use Iterable in your method signature where you now use Seq.

Randall Schulz
I'm running into trouble with this approach: `polymorphic expression cannot be instantiated to expected type; found : => (Array[Char], (Char) => Boolean) => Array[Char] required: (Iterable[Char], (Char) => Boolean) => Iterable[Char]`. If I leave the `[Char]` out I get the same message with `Iterable[?]`. So it does not seem like the implicits are being applied.
huynhjl
+1  A: 

I'd use scala.collection.Seq on Scala 2.8, as this type is parent to all ordered collections. Except Array and String, unfortunately. One can get around that with view bounds, like this:

def test
  [A, CC <% scala.collection.Seq[A]]
  (input: CC, expected: CC)
  (func: (CC, A => Boolean) => CC, predicate: A => Boolean): Unit = {
  def times(n: Int)(f: => Unit) = 1 to n foreach { count => f }
  def testFunction = assert(func(input, predicate) == expected)
  def warm = times(50) { testFunction }
  def test = times(50) { testFunction }

  warm
  val start = System.currentTimeMillis()
  test
  val end = System.currentTimeMillis()
  println("Total time "+(end - start))
}

I'm currying this function so that the input (and expected) can be used to infer the type. Anyway, this won't work with your own Array version on Scala 2.8, because that requires a Manifest. I'm sure it can be provided here somehow, but I don't see quite how.

But say you ignore all that stuff about sequences, arrays, etc. Just remove the view bound from the function, and you get this:

def test
  [A, CC]
  (input: CC, expected: CC)
  (func: (CC, A => Boolean) => CC, predicate: A => Boolean): Unit = {
  def times(n: Int)(f: => Unit) = 1 to n foreach { count => f }
  def testFunction = assert(func(input, predicate) == expected)
  def warm = times(50) { testFunction }
  def test = times(50) { testFunction }

  warm
  val start = System.currentTimeMillis()
  test
  val end = System.currentTimeMillis()
  println("Total time "+(end - start))
}

Which will work just as find. As long the type match, it isn't really relevant for this program to know what a CC is.

Daniel
First, thanks for actually materializing what I'm just suggesting in the question, as well as suggesting A and CC as unrelated types tied together by func. The only issue I ran into is that the in-place Array solution does not mesh well with running the function multiple times, as the output becomes the input from the next time. I tried creating a new array copy in the shift function, but then ran into issue with Manifest: `could not find implicit value for evidence parameter of type Manifest[T]`.
huynhjl
@huynhjl Yes, working with arrays on Scala 2.8 can be tough. I was thinking that you likely would have to make the function curried, passing in the first parameter list an explicit `Manifest[T]`, and then pass that parameter where necessary. And, when calling `test`, pass the explicit `Manifest` for it too. Make another question, and I'll look into this.
Daniel