function working on functions of Array[T] or List[T] or Iterable[T]
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 shiftElem开发者_运维知识库ents[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?
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'))
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.
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.
精彩评论