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): How would be a functional approach to shifting certain array elements?
I’d use
scala.collection.Seqon Scala 2.8, as this type is parent to all ordered collections. ExceptArrayandString, unfortunately. One can get around that with view bounds, like this: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
Arrayversion on Scala 2.8, because that requires aManifest. 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:
Which will work just as find. As long the type match, it isn’t really relevant for this program to know what a
CCis.