2013-03-29 11 views
7

Ich habe eine rekursive Funktion, die ich @tailrec machen möchte, indem ich den inneren rekursiven Teil (countR3) Elemente zu einer Warteschlange hinzufügen (agenda ist ein scala.collections.mutable.Queue). Meine Idee ist, dann den äußeren Teil der Funktion über die Tagesordnung zu legen und die Ergebnisse zusammenzufassen.Gibt es eine elegante Möglichkeit, auf einer wachsenden scala.collections.mutable.Queue foldLeft?

HINWEIS: Diese war ein Hausaufgaben-Problem, also möchte ich nicht den ganzen Code posten; Allerdings war die Durchführung der Tail-rekursive Implementierung nicht Teil der Hausaufgabe.

Hier ist der Teil des Codes ist relevant für meine Frage:

import scala.collection.mutable.Queue 

val agenda: Queue[Tuple2[Int, List[Int]]] = Queue() 

@tailrec 
def countR3(y: Int, x: List[Int]): Int = { 
    if (y == 0) 1 
    else if (x.isEmpty) 0 
    else if … 
    else { 
    agenda.enqueue((y - x.head, x)) 
    countR3(y, x.tail) 
    } 
} 
⋮ 
agenda.enqueue((4, List(1, 2))) 
val count = agenda.foldLeft(0) { 
    (count, pair) => { 
    val mohr = countR3(pair._1, pair._2) 
    println("count=" + count + " countR3=" + mohr) 
    count + mohr 
    } 
} 
println(agenda.mkString(" + ")) 
count 

Diese fast scheint ... Das Problem zu arbeiten, ist, dass es nicht auf die Tagesordnung gesetzt alle Elemente nicht iterieren, aber es verarbeitet einige von ihnen. Sie können dies in der Ausgabe siehe unten: [. Von den sechs Punkten der endgültigen Tagesordnung, nur die ersten drei verarbeitet wurden]

count=0 countR3=0 
count=0 countR3=0 
count=0 countR3=0 
(4,List(1, 2)) + (3,List(1, 2)) + (2,List(2)) + (2,List(1, 2)) + (1,List(2)) + (0,List(2)) 

Ich bin im Allgemeinen gut über die Gefahren von mutierenden eine Sammlung, während Sie in Java über iterieren. Aber eine Warteschlange ist ein Pferd von einer anderen Farbe. Natürlich, ich verstehe ich einfach eine dringend notwendige Schleife, wie so schreiben könnte: ein funktionell elegant Weg ist

Das funktioniert sehr gut, aber das ist Scala, erforsche mich, um zu sehen, ob es
var count = 0 
while (!agenda.isEmpty) { 
    val pair = agenda.dequeue() 
    count += countR3(pair._1, pair._2) 
} 

.

Irgendwelche Vorschläge?

Antwort

2

Obwohl wahrscheinlich nicht ganz idiomatischen, könnten Sie dies versuchen:

Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }). 
    takeWhile(_.isDefined).flatten. 
    map({ case (x, y) => countR3(x, y) }). 
    toList.sum 
  • Die Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }) gibt Ihnen einen unendlichen Strom von Warteschlange in Option[Tuple2[Int, List[Int]]] verpackt werden.
  • Dann sobald takeWhile(_.isDefined) schneidet die Sequenz wie die erste None Element angetroffen wird, das heißt, wenn die Warteschlange erschöpft ist.
  • Da der vorherige Anruf noch eine Folge von Option s ergibt, müssen wir sie mit flatten auszupacken.
  • map({ case (x, y) => countR3(x, y) }) im Grunde gilt Ihre Funktion für jedes Element.
  • Und schließlich zwingt toList die Auswertung eines Stroms (das ist, was wir mit gearbeitet haben) und berechnet dann sum die Summe der Elemente der Liste.

Ich denke, das Problem mit agenda.foldLeft (dass es nur ‚einige‘ Warteschlange gestellt Elemente verarbeitet) ist, ich würde vermuten, dass es eine nimmt (wahrscheinlich unveränderlich) Liste der derzeit die Warteschlange gestellt Elemente und daher nicht durch Gegenstände beeinträchtigt wird Diese wurden während der Berechnung hinzugefügt.

+0

Nicht gerade idiomatisch, würde ich zustimmen, aber es scheint, wie es das Gesetz des Seins mehr funktionell rein passen würde. Gute Antwort! Und interessanterweise: Agenda.FoldLeft verarbeitete mehr als nur die ursprünglich eingereihten Gegenstände; es schien nicht einfach an einer Kopie der ursprünglichen Warteschlange zu arbeiten. Vielleicht wurde es gestoppt, als der "letzte" Eintrag dazu führte, dass mehr Einträge zur Warteschlange hinzugefügt wurden, oder so ähnlich. –

Verwandte Themen