2013-03-29 1 views
7

У меня есть рекурсивная функция, которую я пытаюсь сделать @tailrec, имея внутреннюю, рекурсивную часть (countR3) добавить элементы в очередь (agenda - scala.collections.mutable.Queue). Моя идея заключается в том, чтобы затем внешняя часть функции сломалась над повесткой дня и подытожила результаты.Есть ли элегантный способ foldLeft на растущем scala.collections.mutable.Queue?

ПРИМЕЧАНИЕ: Этот был проблемой домашней работы, поэтому я не хочу публиковать весь код; однако, реализация хвостовой рекурсии была не часть домашней работы.

Вот часть кода отношение к моему вопросу:

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 

Это почти, кажется, работает ... Проблема заключается в том, что она не перебирать все элементы включены в повестку дня, но он обрабатывает из них. Вы можете увидеть это в приведенном ниже примере: [. Из шести пунктов окончательной повестки дня первые три были обработаны только]

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

Я обычно хорошо осведомлены об опасностях мутирует когда вы повторяете его, скажем, Java. Но очередь - это своего рода лошадь другого цвета. Конечно, я понимаю, что я мог бы просто написать императивный цикл, например, так:

var count = 0 
while (!agenda.isEmpty) { 
    val pair = agenda.dequeue() 
    count += countR3(pair._1, pair._2) 
} 

Это работает отлично, но это время Scala, я исследую, чтобы увидеть, если есть более функционально элегантный путь.

Любые предложения?

ответ

2

Хотя, вероятно, не совсем идиоматическое, вы можете попробовать это:

Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }). 
    takeWhile(_.isDefined).flatten. 
    map({ case (x, y) => countR3(x, y) }). 
    toList.sum 
  • Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }) дает бесконечный поток элементов очереди, завернутые в Option[Tuple2[Int, List[Int]]].
  • Затем takeWhile(_.isDefined) отрезает последовательность, как только первый элемент None встречается, то есть когда очередь исчерпана.
  • Поскольку предыдущий вызов по-прежнему дает последовательность Option s, нам необходимо развернуть их с помощью flatten.
  • map({ case (x, y) => countR3(x, y) }) в основном применяет вашу функцию к каждому элементу.
  • И, наконец, toList заставляет оценивать поток (именно с этим мы работаем), а затем sum вычисляет сумму элементов списка.

Я думаю, что проблема с agenda.foldLeft (что он обрабатывает только «некоторые» вещи в очереди), я предположил бы, что он принимает (вероятно, неизменный) список текущих элементов установлены в очереди, и, следовательно, не зависит от деталей которые были добавлены во время расчета.

+0

Не совсем идиоматично, я бы согласился, но похоже, что это соответствовало бы чисто функциональности. Хороший ответ! И интересно, «повестка дня».foldLeft' обрабатывал больше, чем только первоначально выставленные предметы; казалось, что он просто не работал над копией начальной очереди. Возможно, это остановилось, когда «последний» элемент привел к добавлению большего количества элементов в очередь или что-то в этом роде. –