Я пытаюсь обобщить повторяющийся, вложенный flatMap
, но не уверен, что он существует.
В следующем коде будут созданы все комбинации n, выбираем 3, :
def choose3flatMap(n: Int, r: Int = 3) =
(0 to n - r)
.flatMap(i => (i + 1 to n - (r - 1))
.flatMap(j => (j + 1 to n - (r - 2))
.map(k => Seq(i, j, k))))
Повторяя операцию flatMap, мы можем получить все комбинации n, выбираем 5, :
def choose5flatMap(n: Int, r: Int = 5) =
(0 to n - r)
.flatMap(i => (i + 1 to n - (r - 1))
.flatMap(j => (j + 1 to n - (r - 2))
.flatMap(k => (k + 1 to n - (r - 3))
.flatMap(l => (l + 1 to n - (r - 4))
.map(m => Seq(i, j, k, l, m)))))
Ясно, что здесь есть шаблон. Я хотел бы использовать это сходство, чтобы получить общее решение для n выбрать r, . Есть ли простой способ достичь этого. Возможно, какая-то функция более высокого порядка?
То, что я пробовал:
Scala позволяет мне переписать map
/flatMap
с помощью выражения for. Это считывает очиститель, но количество вариантов по-прежнему жестко закодировано.
def choose3Loop(n: Int, r: Int = 3) =
for {
i <- 0 to n - r
j <- i + 1 to n - (r - 1)
k <- j + 1 to n - (r - 2)
} yield Seq(i, j, k)
Я могу написать рекурсивное решение непосредственно с помощью flatMap
или с использованием сахара выражения for
:
def combinationsRecursive(n: Int, r: Int, i: Int = 0): Seq[Seq[Int]] =
if (r == 1) (i until n).map(Seq(_))
else {
(i to n - r).flatMap(
i => combinationsRecursive(n, r - 1, i + 1).map(j => i +: j))
}
def combinationsRecursiveLoop(n: Int, r: Int, i: Int = 0): Seq[Seq[Int]] =
if (r == 1) (i until n).map(Seq(_))
else
for {
i <- i to n - r
j <- combinationsRecursiveLoop(n, r - 1, i + 1)
} yield i +: j
Хотя это решения общей проблемы, я задаюсь вопросом, есть ли абстракция более высокого уровня, которую я здесь отсутствует, что может быть применимо и к другим проблемам. Я признаю, что для этого конкретного приложения я мог бы сделать (0 to n).combinations(r)
, чтобы использовать библиотечную реализацию комбинаций вычислений.
В то время как вышеприведенный код Scala, в этом случае меня интересует его функциональный аспект, а не возможности языка. Если есть решение, но которое не поддерживается Scala, меня это интересует.
Изменить: Он является образцом для вызова и результирующий вывод по запросу:
scala > combinationRecursiveLoop (5, 3)
res0: Seq [Seq [Int]] = Вектор (список (0, 1, 2), список (0, 1, 3), список (0, 1, 4), список (0, 2, 3), Список (0, 2, 4), Список (0, 3, 4), Список (1, 2, 3), Список (1, 2, 4), Список (1, 3, 4), Список (2, 3, 4))
scala > combinationRecursiveLoop (5, 3).map( "(" + _. mkString ( "," ) + ")" ). mkString ("")
res1: String = (0, 1, 2) (0, 1, 3) (0, 1, 4) (0, 2, 3) (0, 2, 4) (0, 3, 4) 1, 2, 3) (1, 2, 4) (1, 3, 4) (2, 3, 4)
Он просто предоставляет все подмножества r-элементов набора целых чисел, начиная с нуля, содержащего n элементов. Подробнее о комбинациях можно найти в Википедии.