2010-02-12 3 views
4

J'essayais d'écrire une fonction de test/timing pour les réponses fournies dans this SO question. Certaines réponses fonctionnent sur Array[T], certaines sur List[T], une sur Iterable[T] et une sur String! Ce que je voudrais écrire est une fonction qui prend les fonctions shift* de la question ou les réponses, une liste d'entrée, un prédicat et une sortie attendue et exécuter la fonction. Un peu comme:Fonction travaillant sur les fonctions de Array [T] ou List [T] ou Iterable [T]

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 
} 

Sauf que je peux comprendre la signature, comme Array semble avoir des propriétés mutables Seq, alors que List semble avoir Seq immuables propriétés.

Quelle est la meilleure façon de gérer cela?

Modifier: En utilisant la suggestion de Thomas c'est à quel point je peux obtenir (fonctionne sur Array[Char], List[T] mais pas sur 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] 

Je marquerai la réponse de Daniel acceptée telle qu'elle compile et fournit moi une manière différente de réaliser ce que je voulais - à moins que la méthode sur Array [T] crée un nouveau tableau (et apporte des problèmes Manifest).

(2): How would be a functional approach to shifting certain array elements?

Répondre

1

J'utiliserais scala.collection.Seq sur Scala 2.8, car ce type est parent de toutes les collections ordonnées. Sauf Array et String, malheureusement. On peut se déplacer qu'avec des bornes de vue, comme ceci:

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)) 
} 

Je suis taitement cette fonction pour que l'entrée (et attendu) peut être utilisé pour déduire le type. Quoi qu'il en soit, cela ne fonctionnera pas avec votre propre version Array sur Scala 2.8, car cela nécessite un Manifest. Je suis sûr que cela peut être fourni ici, mais je ne vois pas comment.

Mais que vous ignorez tout ce genre de choses sur les séquences, les tableaux, etc. Il suffit de supprimer la vue liée de la fonction, et vous obtenez ceci:

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)) 
} 

qui fonctionnera aussi trouver. Tant que le type correspond, ce n'est pas vraiment important pour ce programme de savoir ce qu'est un CC.

+0

Tout d'abord, merci de concrétiser ce que je suggère dans la question, et de suggérer A et CC comme des types non liés liés par func. Le seul problème que j'ai rencontré est que la solution Array sur place ne correspond pas bien à l'exécution de la fonction plusieurs fois, car la sortie devient l'entrée de la prochaine fois. J'ai essayé de créer une nouvelle copie de tableau dans la fonction de décalage, mais j'ai ensuite rencontré un problème avec Manifest: 'Impossible de trouver une valeur implicite pour le paramètre evidence du type Manifest [T]'. – huynhjl

+0

@huynhjl Oui, travailler avec des tableaux sur Scala 2.8 peut être difficile. Je pensais que vous auriez probablement à rendre la fonction curry, en passant dans la première liste de paramètres un 'Manifest [T]' explicite, et ensuite passer ce paramètre si nécessaire. Et, quand vous appelez 'test', passez le' Manifest' explicite pour cela aussi. Faites une autre question, et j'examinerai ceci. –

3

Une façon est de définir la fonction:

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')) 
+0

Je voulais vraiment dire 'test' pour appeler' func (input, predicate) '. Cela provoque un 'found: input.type, requis: I'. Je peux corriger cela en changeant à 'input: I' dans la déclaration' test'. Ensuite, j'obtiens des arguments de type inférés [Array [Char], Char] qui ne sont pas conformes aux limites des paramètres de type du test de méthode [I <: Traversable [T], T] 'pour l'exemple' Array [Char] '. L'exemple 'String' a aussi un problème. Les 'List' et' Iterable' fonctionnent. – huynhjl

+0

Vous pouvez supprimer la contrainte I <: Traversable [T].Vous pouvez le faire aussi bien pour les types de retour (func: (I, T => Boolean) => I et attendu: I). –

1

Tous les types que vous mentionnez (même chaîne) sont eithr explicitement (Liste) ou implicitement (Array et String) Iterable, donc tout ce que vous avez à faire est d'utiliser Iterable dans votre signature de méthode où vous utilisez maintenant Seq.

+0

Je rencontre des problèmes avec cette approche: 'l'expression polymorphique ne peut pas être instanciée au type attendu; trouvé: => (Array [Char], (Char) => Boolean) => Array [Char] requis: (Iterable [Char], (Char) => Boolean) => Iterable [Char] '. Si je laisse le '[Char]' sortir, je reçois le même message avec 'Iterable [?]'. Donc, il ne semble pas que les implicits sont appliqués. – huynhjl

Questions connexes