2016-11-26 1 views
1

Je suis nouveau à scala. J'ai besoin de compter le nombre de catégories dans la liste, et j'essaye de construire une fonction récursive de queue, sans aucun succès.Comment compter le nombre total d'articles pour lesquels une classe fait référence

case class Category(name:String, children: List[Category]) 

val lists = List(
    Category("1", 
    List(Category("1.1", 
     List(Category("1.2", Nil)) 
    )) 
) 
    ,Category("2", Nil), 
    Category("3", 
    List(Category("3.1", Nil)) 
) 
) 
+0

Qu'avez-vous essayé? Quel est votre problème concernant la fonction récursive de la queue? – marstran

+2

Rendre la queue récursive pourrait être difficile puisque vous traversez une arborescence comme la structure de données. Fondamentalement, la seule façon de le faire est de garder une liste de tout le travail à faire (par exemple, être de type List [catégorie] qui serait alors utilisé comme une pile) et en faire un argument pour la fonction. – SpiderPig

+1

@SpiderPig Vous avez seulement besoin d'un accumulateur qui contient le nombre actuel. – marstran

Répondre

1

solution de Nyavro peut être beaucoup plus rapide (de plusieurs ordres de grandeur) si vous utilisez des listes au lieu des cours d'eau et ajouter également des éléments à l'avant. Cela est dû au fait que x.children est généralement beaucoup plus court que xs et que la liste de Scala est une liste unidirectionnelle immuable qui rend les opérations d'avance beaucoup plus rapides que les opérations d'ajout. Voici un exemple

import scala.annotation.tailrec 

case class Category(name:String, children: List[Category]) 

@tailrec 
def childCount(cats:Stream[Category], acc:Int):Int = 
    cats match { 
    case Stream.Empty => acc 
    case x #:: xs => childCount(xs ++ x.children, acc+1) 
    } 

@tailrec 
def childCount2(cats: List[Category], acc:Int): Int = 
    cats match { 
    case Nil => acc 
    case x :: xs => childCount2(x.children ++ xs, acc + 1) 
    } 

def generate(depth: Int, children: Int): List[Category] = { 
    if(depth == 0) Nil 
    else (0 until children).map(i => Category("abc", generate(depth - 1, children))).toList 
} 

val list = generate(8, 3) 

var start = System.nanoTime 
var count = childCount(list.toStream, 0) 
var end = System.nanoTime 
println("count: " + count) 
println("time: " + ((end - start)/1e6) + "ms") 

start = System.nanoTime 
count = childCount2(list, 0) 
end = System.nanoTime 
println("count: " + count) 
println("time: " + ((end - start)/1e6) + "ms") 

sortie:

count: 9840 
time: 2226.761485ms 
count: 9840 
time: 3.90171ms 
+0

Merci pour l'aide, je l'apprécie vraiment. J'étais coincé alors que j'essayais de le résoudre avec la fonction de taille de List, essayant ainsi d'éviter de parcourir chaque élément de la liste. Mais je crois que c'est une meilleure solution et ira avec. – AjitChahal

1

Envisagez l'idée suivante.

Permet de définir la fonction childCount, en prenant la collecte des catégories (chats) et le nombre d'enfants comptent jusqu'à présent (acc). Pour organiser le traitement récursif de la queue, nous prenons le premier enfant de la collection et augmentons l'acc. Nous avons donc traité le premier élément, mais nous avons obtenu plus d'éléments à traiter - les enfants du premier élément. L'idée est de mettre ces enfants non traités à la fin de la collecte des enfants et d'appeler à nouveau childCount.

Vous pouvez la mettre en œuvre ainsi:

@tailrec 
def childCount(cats:Stream[Category], acc:Int):Int = 
    cats match { 
    case Stream.Empty => acc 
    case x #:: xs => childCount(xs ++ x.children, acc+1) 
    } 

l'appellent:

val count = childCount(lists.toStream, 0) 
+0

Cela fonctionnera extrêmement lent si vous avez un grand nombre de catégories à compter. Pour le rendre rapide, remplacez 'Stream [Category]' par 'List [Category]', 'x # :: xs' par' x :: xs' et 'xs ++ x.children' par' x.children ++ xs '. – SpiderPig

+0

Pourquoi pensez-vous que l'implémentation basée sur List va fonctionner plus vite? Pourquoi pensez-vous que l'insertion de x.children au début est préférable à l'ajout à la fin? Quoi qu'il en soit, la concaténation des Listes «déroule» la première liste et insère des éléments au début de la seconde. La concatination des flots n'est-elle pas paresseuse? – Nyavro

+0

Lazy ne signifie pas toujours plus vite. De plus, si vous concaténez une liste à un flux, vous avez de toute façon tous les éléments en mémoire malgré la paresse. J'ai testé la vitesse et j'ai posté une réponse avec mes résultats. – SpiderPig