Изучая, как выполнять динамическое программирование в Scala, я часто оказываюсь в ситуации, когда я хочу рекурсивно работать с массивом (или некоторыми другими итерируемыми) элементами.Когда я делаю это, я, как правило, пишу громоздкие функции, например:
def arraySum(array: Array[Int], index: Int, accumulator: Int): Int => {
if (index == array.length) {
accumulator
} else {
arraySum(array, index + 1, accumulator + array(index)
}
}
arraySum(Array(1,2,3), 0, 0)
(на мгновение не обращайте внимания на то, что я могу просто вызвать sum
в массиве или сделать .reduce(_ + _)
, я пытаюсьизучить принципы программирования.)
Но похоже, что я передаю много переменных, и что именно является точкой передачи массива для каждого вызова функции?Это кажется нечистым.
Так что вместо этого у меня появилась идея сделать это с итераторами и не беспокоиться о передаче индексов:
def arraySum(iter: Iterator[Int])(implicit accumulator: Int = 0): Int = {
try {
val nextInt = iter.next()
arraySum(iter)(accumulator + nextInt)
} catch {
case nee: NoSuchElementException => accumulator
}
}
arraySum(Array(1,2,3).toIterator)
Это кажется гораздо более чистым решением.Однако это разваливается, когда вам нужно использовать динамическое программирование для исследования некоторого пространства результатов, и вам не нужно вызывать итератор при каждом вызове функции.Например,
def explore(iter: Iterator[Int])(implicit accumulator: Int = 0): Int = {
if (someCase) {
explore(iter)(accumulator)
} else if (someOtherCase){
val nextInt = iter.next()
explore(iter)(accumulator + nextInt)
} else {
// Some kind of aggregation/selection of explore results
}
}
Насколько я понимаю, итератор iter
здесь функционирует как передача по ссылке, поэтому, когда эта функция вызывает iter.next()
, это изменяет экземпляр iter, который передается всем другим рекурсивным вызовамфункция.Чтобы обойти это, теперь я клонирую итератор при каждом вызове функции explore
.Например:
def explore(iter: Iterator[Int])(implicit accumulator: Int = 0): Int = {
if (someCase) {
explore(iter)(accumulator)
} else if (someOtherCase){
val iterClone = iter.toList.toIterator
explore(iterClone)(accumulator + iterClone.next())
} else {
// Some kind of aggregation/selection of explore results
}
}
Но это кажется довольно глупым, и глупость возрастает, когда у меня есть несколько итераторов, которые могут или не могут нуждаться в клонировании в нескольких else if
случаях.Как правильно обращаться с подобными ситуациями?Как я могу элегантно решить такие проблемы?